Updates for Videos

This commit is contained in:
Deon George 2019-12-15 23:34:42 +11:00
parent 075d31e9f4
commit 49933382f3
14 changed files with 376 additions and 254 deletions

View File

@ -6,9 +6,6 @@ use Illuminate\Console\Command;
class CatalogScan extends Command
{
// Our photo object
private $o = NULL;
/**
* The name and signature of the console command.
*
@ -72,8 +69,10 @@ class CatalogScan extends Command
$o->scanned = '1';
if ($o->getDirty())
if ($o->getDirty()) {
$this->warn(sprintf('Image [%s] metadata changed',$o->file_path()));
//dump($o->getDirty());
}
$o->save();
}

View File

@ -2,7 +2,6 @@
namespace App\Console\Commands;
use Illuminate\Support\Facades\Log;
use Illuminate\Console\Command;
use Illuminate\Foundation\Bus\DispatchesJobs;

View File

@ -2,17 +2,16 @@
namespace App\Console\Commands;
use Log;
use Illuminate\Console\Command;
use Illuminate\Foundation\Bus\DispatchesJobs;
use App\Model\Video;
use App\Model\Tag;
use App\Model\Person;
use App\Models\{Person,Video,Tag};
use App\Traits\Files;
class VideoImport extends Command
{
use DispatchesJobs;
use \App\Traits\Files;
use Files;
/**
* The name and signature of the console command.
@ -52,7 +51,11 @@ class VideoImport extends Command
*/
public function handle()
{
$files = $this->getFiles(['dir'=>$this->option('dir'),'file'=>$this->option('file')],'video');
$files = $this->getFiles([
'dir'=>$this->option('dir'),
'file'=>$this->option('file')
],'video');
if (! count($files))
exit;
@ -64,22 +67,21 @@ class VideoImport extends Command
$t = $p = array();
// Tags
if ($this->option('tags'))
{
if ($this->option('tags')) {
$tags = explode(',',$this->option('tags'));
$t = Tag::whereIn('tag',$tags)->pluck('id')->toArray();
if (! $t OR count($t) != count($tags))
{
if (! $t OR count($t) != count($tags)) {
$this->error(sprintf('Tag [%s] dont exist',join('|',$tags)));
abort(501);
}
}
// People
if ($this->option('people'))
{
if ($this->option('people')) {
$tags = explode(',',$this->option('people'));
$p = Person::whereIn('tag',$tags)->pluck('id')->toArray();
if (! $p OR count($p) != count($tags))
{
$this->error(sprintf('People [%s] dont exist',join('|',$tags)));
@ -88,18 +90,15 @@ class VideoImport extends Command
}
$c = 0;
foreach ($files as $file)
{
foreach ($files as $file) {
$bar->advance();
if (preg_match('/@__thumb/',$file) OR preg_match('/\/._/',$file))
{
if (preg_match('/@__thumb/',$file) OR preg_match('/\/._/',$file)) {
$this->warn(sprintf('Ignoring file [%s]',$file));
continue;
}
if (! in_array(strtolower(pathinfo($file,PATHINFO_EXTENSION)),config('video.import.accepted')))
{
if (! in_array(strtolower(pathinfo($file,PATHINFO_EXTENSION)),config('video.import.accepted'))) {
$this->warn(sprintf('Ignoring [%s]',$file));
continue;
}
@ -111,21 +110,21 @@ class VideoImport extends Command
$o = Video::where('filename',$file)->first();
if (is_null($o))
{
// The video doesnt exist
if (is_null($o)) {
$o = new Video;
$o->filename = $file;
}
if (! is_readable($o->file_path()))
{
if ($o->exists)
$this->warn(sprintf('%s [%s] already in DB: %s',$o->objectType(),$file,$o->id));
// Make sure we can read the video.
if (! is_readable($o->file_path())) {
$this->warn(sprintf('Ignoring [%s], it is not readable',$o->file_path()));
continue;
}
if ($o->exists)
$this->warn(sprintf('%s [%s] already in DB: %s',$o->objectType(),$file,$o->id));
$o->save();
if ($o->wasRecentlyCreated)

View File

@ -12,18 +12,18 @@ class PhotoController extends Controller
protected $list_duplicates = 20;
protected $list_deletes = 50;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
public function delete($id)
{
public function delete($id)
{
$po = Photo::notRemove()->findOrFail($id);
if ($po)
@ -33,18 +33,18 @@ class PhotoController extends Controller
}
return redirect()->action('PhotoController@info',[$id]);
}
}
public function deletes($id=NULL)
{
public function deletes($id=NULL)
{
return view('catalog.deletereview',[
'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)
]);
}
}
public function deletesUpdate(Request $request)
{
public function deletesUpdate(Request $request)
{
foreach ($request->input('remove') as $id=>$k)
{
$o = Photo::findOrFail($id);
@ -54,18 +54,18 @@ class PhotoController extends Controller
}
return redirect()->action('PhotoController@deletes',$request->input('pagenext') ? '?page='.$request->input('pagenext') : NULL);
}
}
public function duplicates($id=NULL)
{
public function duplicates($id=NULL)
{
return view('catalog.duplicatereview',[
'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)
]);
}
}
public function duplicatesUpdate(Request $request)
{
public function duplicatesUpdate(Request $request)
{
foreach ($request->input('items') as $id)
{
$po = Photo::findOrFail($id);
@ -83,20 +83,20 @@ class PhotoController extends Controller
}
return redirect()->action('PhotoController@duplicates','?page='.$request->input('page'));
}
}
public function info(Photo $o)
{
public function info(Photo $o)
{
return view('photo.view',['o'=>$o]);
}
}
public function thumbnail($id)
{
public function thumbnail($id)
{
return response(Photo::findOrFail($id)->thumbnail(TRUE))->header('Content-Type','image/jpeg');
}
}
public function undelete($id)
{
public function undelete($id)
{
$po = Photo::findOrFail($id);
if ($po)
@ -106,10 +106,10 @@ class PhotoController extends Controller
}
return redirect()->action('PhotoController@info',[$id]);
}
}
public function view($id)
{
public function view($id)
{
return response(Photo::findOrFail($id)->image())->header('Content-Type','image/jpeg');
}
}
}

View File

@ -2,28 +2,26 @@
namespace App\Http\Controllers;
use Illuminate\Http\Response;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Model\Video;
use App\Models\Video;
use App\Jobs\VideoDelete;
use App\Helpers\VideoStream;
class VideoController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
public function delete($id)
{
public function delete($id)
{
$po = Video::notRemove()->findOrFail($id);
if ($po)
@ -33,15 +31,15 @@ class VideoController extends Controller
}
return redirect()->action('VideoController@info',[$id]);
}
}
public function deletes($id=NULL)
{
public function deletes($id=NULL)
{
return view('catalog.deletereview',['return'=>url('v/deletes'),'catalog'=>is_null($id) ? Video::where('remove',1)->paginate(50) : Video::where('id',$id)->paginate(1)]);
}
}
public function deletesUpdate(Request $request)
{
public function deletesUpdate(Request $request)
{
foreach ($request->input('remove') as $k=>$id)
{
$o = Video::findOrFail($k);
@ -51,15 +49,15 @@ class VideoController extends Controller
}
return redirect()->action('VideoController@deletes',$request->input('pagenext') ? '?page='.$request->input('pagenext') : NULL);
}
}
public function duplicates($id=NULL)
{
public function duplicates($id=NULL)
{
return view('catalog.duplicatereview',['return'=>url('/v/duplicates'),'catalog'=>is_null($id) ? Video::notRemove()->where('duplicate',1)->paginate(50) : Video::where('id',$id)->paginate(1)]);
}
}
public function duplicatesUpdate(Request $request)
{
public function duplicatesUpdate(Request $request)
{
foreach ($request->input('items') as $id)
{
$po = Video::findOrFail($id);
@ -77,14 +75,15 @@ class VideoController extends Controller
}
return redirect()->action('VideoController@duplicates','?page='.$request->input('page'));
}
public function info($id)
{
return view('video.view', ['video'=> Video::findOrFail($id)]);
}
}
public function undelete($id)
{
public function info(Video $o)
{
return view('video.view', ['o'=>$o]);
}
public function undelete($id)
{
$po = Video::findOrFail($id);
if ($po)
@ -94,10 +93,10 @@ class VideoController extends Controller
}
return redirect()->action('VideoController@info',[$id]);
}
}
public function view($id)
{
public function view($id)
{
(new VideoStream(Video::findOrFail($id)->file_path()))->start();
}
}
}
}

View File

@ -2,6 +2,7 @@
namespace App\Models\Abstracted;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Schema;
use DB;
@ -108,7 +109,7 @@ abstract class Catalog extends Model
*/
public function date_taken(): string
{
return $this->date_created ? date('Y-m-d H:i:s',$this->date_created) : 'UNKNOWN';
return $this->date_created ? $this->date_created->format('Y-m-d H:i:s') : 'UNKNOWN';
}
public function device(): string
@ -163,7 +164,7 @@ abstract class Catalog extends Model
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());
: $this->date_created->format('Y/m/d-His')),$this->type());
return (($short OR preg_match('/^\//',$file)) ? '' : config('video.dir').DIRECTORY_SEPARATOR).$file;
}
@ -227,6 +228,10 @@ abstract class Catalog extends Model
return $this->HTMLCheckbox('flag',$this->id,$this->flag);
}
public function getDateCreatedAttribute() {
return Carbon::createFromTimestamp($this->attributes['date_created']);
}
/**
* @deprecated
*/
@ -337,6 +342,8 @@ abstract class Catalog extends Model
->first();
}
abstract public function property(string $property);
/**
* Return my class shortname
*/
@ -368,13 +375,12 @@ abstract class Catalog extends Model
*/
public function signature($short=FALSE)
{
return $short ? static::signaturetrim($this->signature) : $this->signature;
return ($short AND $this->signature) ? static::stringtrim($this->signature) : $this->signature;
}
/**
* Trim a string
*
* @todo rename stringtrim
* @param string $string
* @param int $chrs
* @return string

View File

@ -2,7 +2,6 @@
namespace App\Models;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
class Photo extends Abstracted\Catalog
@ -60,7 +59,7 @@ class Photo extends Abstracted\Catalog
public function getHtmlImageURL(): string
{
return sprintf('<img height="240" src="%s"></img>',url('/p/thumbnail/'.$this->id));
return sprintf('<img class="p-3" src="%s">',url('p/thumbnail',$this->id));
}
/**
@ -143,7 +142,7 @@ class Photo extends Abstracted\Catalog
return $imo->getImageBlob();
}
public function property($property)
public function property(string $property)
{
if (! $this->o())
return NULL;
@ -175,10 +174,6 @@ class Photo extends Abstracted\Catalog
return $this->o() ? $this->_o->getImageProperties() : [];
}
public function getDateCreatedAttribute() {
return Carbon::createFromTimestamp($this->attributes['date_created']);
}
public function setDateCreated()
{
$this->date_created = $this->property('creationdate');

View File

@ -3,16 +3,22 @@
namespace App\Models;
use DB;
use Illuminate\Support\Arr;
class Video extends Abstracted\Catalog
{
// ID3 Object
private $_o = NULL;
public function getIDLinkAttribute()
{
return $this->HTMLLinkAttribute($this->id,url('v/info').'/');
}
public function getIDLinkAttribute()
{
return $this->HTMLLinkAttribute($this->id,url('v/info').'/');
}
public function getHtmlImageURL()
{
return sprintf('<video width="320" height="240" src="%s" controls></video>',url('/v/view/'.$this->id));
}
/**
* Return an GetID3 object or attribute
@ -33,44 +39,45 @@ class Video extends Abstracted\Catalog
return is_null($attr) ? $this->_o : (array_key_exists($attr,$this->_o->info) ? $this->_o->info[$attr] : NULL);
}
public function property($property)
public function property(string $property)
{
if (! $this->o())
return NULL;
switch ($property)
{
switch ($property) {
case 'creationdate':
// Try creation_Data
$x = array_get($this->_o->info,'quicktime.comments.creation_date.0');
$x = Arr::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');
$x = Arr::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 'software': return array_get($this->_o->info,'quicktime.comments.software.0');
case 'signature': return array_get($this->_o->info,'sha1_data');
case 'make': return Arr::get($this->_o->info,'quicktime.comments.make.0');
case 'model': return Arr::get($this->_o->info,'quicktime.comments.model.0');
case 'software': return Arr::get($this->_o->info,'quicktime.comments.software.0');
case 'signature': return Arr::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');
case 'identifier': if ($x=array_get($this->_o->info,'quicktime.comments')) {
return array_get(array_flatten(array_only($x,'content.identifier')),0);
case 'height': return Arr::get($this->_o->info,'video.resolution_y');
case 'width': return Arr::get($this->_o->info,'video.resolution_x');
case 'length': return Arr::get($this->_o->info,'playtime_seconds');
case 'type': return Arr::get($this->_o->info,'video.dataformat');
case 'codec': return Arr::get($this->_o->info,'audio.codec');
case 'audiochannels': return Arr::get($this->_o->info,'audio.channels');
case 'samplerate': return Arr::get($this->_o->info,'audio.sample_rate');
case 'channelmode': return Arr::get($this->_o->info,'audio.channelmode');
case 'gps_lat': return Arr::get($this->_o->info,'quicktime.comments.gps_latitude.0');
case 'gps_lon': return Arr::get($this->_o->info,'quicktime.comments.gps_longitude.0');
case 'gps_altitude': return Arr::get($this->_o->info,'quicktime.comments.gps_altitude.0');
case 'identifier':
if ($x=Arr::get($this->_o->info,'quicktime.comments')) {
return Arr::get(Arr::flatten(Arr::only($x,'content.identifier')),0);
}
break;
default:
return NULL;
}
@ -85,24 +92,23 @@ class Video extends Abstracted\Catalog
* 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)))
{
if (! $data AND is_null($data = Arr::get($this->_o->info,$atom))) {
// Didnt find it.
return NULL;
}
foreach ($paths as $path)
{
foreach ($paths as $path) {
$found = FALSE;
foreach ($data as $array)
{
if ($array['name'] === $path)
{
foreach ($data as $array) {
if ($array['name'] === $path) {
$found = TRUE;
if ($path != last($paths))
$data = $array['subatoms'];
else
$data = $array;
break;
}
}
@ -170,9 +176,4 @@ class Video extends Abstracted\Catalog
{
return strtolower($mime ? File::mime_by_ext(pathinfo($this->filename,PATHINFO_EXTENSION)) : pathinfo($this->filename,PATHINFO_EXTENSION));
}
public function getHtmlImageURL()
{
return sprintf('<video width="320" height="240" src="%s" controls></video>',url('/v/view/'.$this->id));
}
}

View File

@ -11,6 +11,7 @@
"php": "^7.2",
"barryvdh/laravel-debugbar": "^3.2",
"fideloper/proxy": "^4.0",
"james-heinrich/getid3": "^1.9",
"laravel/framework": "^6.4",
"laravel/socialite": "^4.2",
"laravel/tinker": "^1.0",

65
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "99533013200b14683fe672984d68b218",
"content-hash": "680a81e5f8c3bb384cad5608775023d6",
"packages": [
{
"name": "barryvdh/laravel-debugbar",
@ -839,6 +839,69 @@
"description": "Highlight PHP code in terminal",
"time": "2018-09-29T18:48:56+00:00"
},
{
"name": "james-heinrich/getid3",
"version": "v1.9.18",
"source": {
"type": "git",
"url": "https://github.com/JamesHeinrich/getID3.git",
"reference": "0723b77cafe9278618cfb6bc91b75e73705d3de8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/JamesHeinrich/getID3/zipball/0723b77cafe9278618cfb6bc91b75e73705d3de8",
"reference": "0723b77cafe9278618cfb6bc91b75e73705d3de8",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"jakub-onderka/php-parallel-lint": "^0.9 || ^1.0"
},
"suggest": {
"ext-SimpleXML": "SimpleXML extension is required to analyze RIFF/WAV/BWF audio files (also requires `ext-libxml`).",
"ext-com_dotnet": "COM extension is required when loading files larger than 2GB on Windows.",
"ext-ctype": "ctype extension is required when loading files larger than 2GB on 32-bit PHP (also on 64-bit PHP on Windows) or executing `getid3_lib::CopyTagsToComments`.",
"ext-dba": "DBA extension is required to use the DBA database as a cache storage.",
"ext-exif": "EXIF extension is required for graphic modules.",
"ext-iconv": "iconv extension is required to work with different character sets (when `ext-mbstring` is not available).",
"ext-json": "JSON extension is required to analyze Apple Quicktime videos.",
"ext-libxml": "libxml extension is required to analyze RIFF/WAV/BWF audio files.",
"ext-mbstring": "mbstring extension is required to work with different character sets.",
"ext-mysql": "MySQL extension is required to use the MySQL database as a cache storage (deprecated in PHP 5.5, removed in PHP >= 7.0, use `ext-mysqli` instead).",
"ext-mysqli": "MySQLi extension is required to use the MySQL database as a cache storage.",
"ext-rar": "RAR extension is required for RAR archive module.",
"ext-sqlite3": "SQLite3 extension is required to use the SQLite3 database as a cache storage.",
"ext-xml": "XML extension is required for graphic modules to analyze the XML metadata.",
"ext-zlib": "Zlib extension is required for archive modules and compressed metadata."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.9.x-dev"
}
},
"autoload": {
"classmap": [
"getid3/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-1.0-or-later",
"LGPL-3.0-only",
"MPL-2.0"
],
"description": "PHP script that extracts useful information from popular multimedia file formats",
"homepage": "https://www.getid3.org/",
"keywords": [
"codecs",
"php",
"tags"
],
"time": "2019-09-16T19:08:47+00:00"
},
{
"name": "laravel/framework",
"version": "v6.5.0",

View File

@ -8,7 +8,9 @@
Photo #{{ $o->id }}
@endsection
@section('contentheader_description')
@if($o->duplicate)<button class="btn btn-sm btn-warning">DUPLICATE</button>@endif @if($o->remove)<button class="btn btn-sm btn-danger">PENDING DELETE</button>@endif
@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->remove)<button class="btn btn-sm btn-danger">PENDING DELETE</button>@endif
@endsection
@section('page_title')
#{{ $o->id }}
@ -17,7 +19,7 @@
@section('main-content')
<div class="row">
<div class="col-3">
<a href="{{ url('p/view',$o->id) }}"><img class="p-3" src="{{ url('p/thumbnail',$o->id) }}"></a>
<a href="{{ url('p/view',$o->id) }}">{!! $o->getHtmlImageURL() !!}</a>
<span class="pagination justify-content-center">
<nav>
@ -51,15 +53,15 @@
<dt>Date Taken</dt><dd>{{ $o->date_taken() }}<dd>
<dt>Camera</dt><dd>{{ $o->device() }}<dd>
<hr>
<dt>Location</dt><dd>
@if($o->gps() == 'UNKNOWN')
UNKNOWN
@else
<div id="map" style="width: 400px; height: 300px"></div>
@endif
<dt>Location</dt>
<dd>
@if($o->gps() == 'UNKNOWN')
UNKNOWN
@else
<div id="map" style="width: 400px; height: 300px"></div>
@endif
</dd>
<br/>
<hr>
<dt>Exif Data</dt><dd>
<table>
@foreach ($o->properties() as $k => $v)
@ -67,19 +69,28 @@
@endforeach
</table>
</dd>
<hr>
@if($x = $o->duplicates()->get())
<dt>Duplicates</dt>
<dd>
@foreach($x as $oo)
@if(! $loop->first)| @endif
{!! $oo->id_link !!}
@endforeach
</dd>
@endif
</div>
@if ($o->remove)
<form action="{{ url('p/undelete',$o->id) }}" method="POST">
{{ csrf_field() }}
<button class="btn btn-primary">Undelete</button>
@else
<form action="{{ url('p/delete',$o->id) }}" method="POST">
{{ csrf_field() }}
<button class="btn btn-danger">Delete</button>
@endif
{{ csrf_field() }}
</form>
</div>
</div>

View File

@ -18,6 +18,7 @@
<span class="badge badge-warning right">{{ \App\Models\Photo::where('duplicate',TRUE)->count() }}</span>
</a>
</li>
<li class="nav-item">
<a href="{{ url('p/deletes') }}" class="nav-link @if(preg_match('#^p/deletes$/'.'#',request()->path())) active @endif">
<i class="fa fa-calendar nav-icon"></i> <p>Delete</p>
@ -25,4 +26,26 @@
</a>
</li>
</ul>
</li>
<li class="pt-3 nav-item has-treeview @if(preg_match('#^v/(duplicate|delete)s#',request()->path()))menu-open @else menu-closed @endif">
<a href="#" class="nav-link @if(preg_match('#^/(duplicate|delete)s/[0-9]+#',request()->path())) active @endif">
<i class="nav-icon fa fa-video-camera"></i> <p>VIDEOS<i class="fa fa-angle-left right"></i></p>
</a>
<ul class="nav nav-treeview">
<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>
</a>
</li>
<li class="nav-item">
<a href="{{ url('v/deletes') }}" class="nav-link @if(preg_match('#^v/deletes$/'.'#',request()->path())) active @endif">
<i class="fa fa-calendar nav-icon"></i> <p>Delete</p>
<span class="badge badge-danger right">{{ \App\Models\Video::where('remove',TRUE)->count() }}</span>
</a>
</li>
</ul>
</li>

View File

@ -1,90 +1,116 @@
@extends('layouts.app')
@extends('adminlte::layouts.app')
@section('content')
<div class="container">
@section('htmlheader_title')
Video - {{ $o->id }}
@endsection
@section('contentheader_title')
Video #{{ $o->id }}
@endsection
@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->remove)<button class="btn btn-sm btn-danger">PENDING DELETE</button>@endif
@endsection
@section('page_title')
#{{ $o->id }}
@endsection
@section('main-content')
<div class="row">
<div class="col-md-10 col-md-offset-1">
<div class="panel panel-default">
<div class="panel-heading">
Video {{ $video->id }}<?php if ($video->remove) : ?> - <strong>PENDING DELETE</strong><?php endif?><?php if ($video->duplicate) : ?> - <strong>DUPLICATE</strong><?php endif?>
</div>
<div class="col-4">
<a href="{{ url('p/view',$o->id) }}">{!! $o->getHtmlImageURL() !!}</a>
<div class="panel-body">
<div class="col-md-4">
<div class="text-center">
{!! $video->view() !!}
<ul class="pagination">
<li <?php if (! $x = $video->previous()) : ?>class="disabled"<?php endif ?>><a href="{{ $x ? url('/v/info/'.$x->id) : '#' }}">&lt;&lt;</a></li>
<li <?php if (! $x = $video->next()) : ?>class="disabled"<?php endif ?>><a href="{{ $x ? url('/v/info/'.$x->id) : '#' }}">&gt;&gt;</a></li>
</ul>
</div>
<span class="pagination justify-content-center">
<nav>
<ul class="pagination">
<li class="page-item @if(! $x=$o->previous())disabled @endif" aria-disabled="@if(! $x)true @else false @endif" aria-label="&laquo; Previous">
<a class="page-link" href="{{ $x ? url('p/info',$x->id) : '#' }}">&lt;&lt;</a>
</li>
</div>
<div class="col-md-8">
<div class="dl-horizontal">
<dt>Signature</dt><dd>{{ $video->signature(TRUE) }}</dd>
<dt>Filename</dt><dd>{{ $video->file_path(TRUE) }}<dd>
<?php if ($video->shouldMove()) : ?>
<dt>NEW Filename</dt><dd>{{ $video->file_path(TRUE,TRUE) }}<dd>
<?php endif ?>
<dt>Size</dt><dd>{{ $video->file_size() }}<dd>
<dt>Dimensions</dt><dd>{{ $video->width }} x {{ $video->height }}<dd>
<dt>Length</dt><dd>{{ $video->length }}<dd>
<dt>Type</dt><dd>{{ $video->type }}<dd>
<dt>Codec</dt><dd>{{ $video->codec }}<dd>
<dt>Audio Channels</dt><dd>{{ $video->audiochannels }}<dd>
<dt>Channels Mode</dt><dd>{{ $video->channelmode }}<dd>
<dt>Sample Rate</dt><dd>{{ $video->samplerate }}<dd>
<br/>
<dt>Date Taken</dt><dd>{{ $video->date_taken() }}<dd>
<dt>Camera</dt><dd>{{ $video->make }}<dd>
<dt>Model</dt><dd>{{ $video->model }}<dd>
<dt>Software</dt><dd>{{ $video->software }}<dd>
<dt>Identifier</dt><dd>{{ $video->identifier }}<dd>
<br/>
<dt>Location</dt><dd>
<?php if ($video->gps() == 'UNKNOWN') : ?>
UNKNOWN
<?php else : ?>
<div id="map" style="width: 400px; height: 300px"></div>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
var myLatLng = {lat: {{ $video->gps_lat }}, lng: {{ $video->gps_lon }}};
var map = new google.maps.Map(document.getElementById("map"), {
zoom: 16,
center: myLatLng,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var marker = new google.maps.Marker({
map: map,
position: myLatLng,
});
</script>
<?php endif ?>
</dd>
@if($x = $video->list_duplicate())
<dt>Duplicates</dt><dd>
@php
$y=''; foreach($video->list_duplicate() as $id) $y.=($y ? '|' : '').sprintf('<a href="%s">%s</a>',url('/v/info/'.$id),$id);
echo($y);
@endphp
</dd>
@endif
</div>
</div>
<li class="page-item active" aria-current="page"><span class="page-link">{{ $o->id }}</span></li>
<?php if ($video->remove) : ?>
<form action="{{ url('/v/undelete/'.$video->id) }}" method="POST">
<button class="btn btn-default">Undelete</button>
<?php else : ?>
<form action="{{ url('/v/delete/'.$video->id) }}" method="POST">
<button class="btn btn-default">Delete</button>
<?php endif ?>
{{ csrf_field() }}
</form>
</div>
<li class="page-item @if(! $x=$o->next())disabled @endif" aria-disabled="@if(! $x)true @else false @endif" aria-label="&laquo; Previous">
<a class="page-link" href="{{ $x ? url('p/info',$x->id) : '#' }}">&gt;&gt;</a>
</li>
</ul>
</nav>
</span>
</div>
<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>
@if ($o->shouldMove())
<dt>NEW Filename</dt><dd>{{ $o->file_path(TRUE,TRUE) }}<dd>
@endif
<dt>Size</dt><dd>{{ $o->file_size() }}<dd>
<dt>Dimensions</dt><dd>{{ $o->dimensions }}<dd>
<dt>Length</dt><dd>{{ $o->length }}<dd>
<dt>Type</dt><dd>{{ $o->type }}<dd>
<dt>Codec</dt><dd>{{ $o->codec }}<dd>
<dt>Audio Channels</dt><dd>{{ $o->audiochannels }}<dd>
<dt>Channels Mode</dt><dd>{{ $o->channelmode }}<dd>
<dt>Sample Rate</dt><dd>{{ $o->samplerate }}<dd>
<hr>
<dt>Date Taken</dt><dd>{{ $o->date_taken() }}<dd>
<dt>Camera</dt><dd>{{ $o->make }}<dd>
<dt>Model</dt><dd>{{ $o->model }}<dd>
<dt>Software</dt><dd>{{ $o->software }}<dd>
<dt>Identifier</dt><dd>{{ $o->identifier }}<dd>
<hr>
<dt>Location</dt>
<dd>
@if($o->gps() == 'UNKNOWN')
UNKNOWN
@else
<div id="map" style="width: 400px; height: 300px"></div>
@endif
</dd>
@if($x = $o->duplicates()->get())
<dt>Duplicates</dt>
<dd>
@foreach($x as $oo)
@if(! $loop->first)| @endif
{!! $oo->id_link !!}
@endforeach
</dd>
@endif
</div>
@if ($o->remove)
<form action="{{ url('v/undelete',$o->id) }}" method="POST">
<button class="btn btn-primary">Undelete</button>
@else
<form action="{{ url('v/delete',$o->id) }}" method="POST">
<button class="btn btn-danger">Delete</button>
@endif
{{ csrf_field() }}
</form>
</div>
</div>
</div>
@endsection
@section('page-scripts')
@if($o->gps() !== 'UNKNOWN')
@js('//maps.google.com/maps/api/js?sensor=false')
<script type="text/javascript">
var myLatLng = {lat: {{ $o->gps_lat }}, lng: {{ $o->gps_lon }}};
var map = new google.maps.Map(document.getElementById("map"), {
zoom: 16,
center: myLatLng,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var marker = new google.maps.Marker({
map: map,
position: myLatLng,
});
</script>
@endif
@append

View File

@ -18,16 +18,16 @@ Route::get('/p/duplicates/{id?}','PhotoController@duplicates')->where('id','[0-9
Route::get('/v/duplicates/{id?}','VideoController@duplicates')->where('id','[0-9]+');
Route::get('/p/info/{o}','PhotoController@info')->where('o','[0-9]+');
Route::get('/v/info/{id}','VideoController@info')->where('id','[0-9]+');
Route::get('/v/info/{o}','VideoController@info')->where('o','[0-9]+');
Route::get('/p/thumbnail/{o}','PhotoController@thumbnail')->where('o','[0-9]+');
Route::get('/p/view/{o}','PhotoController@view')->where('o','[0-9]+');
Route::get('/v/view/{id}','VideoController@view')->where('id','[0-9]+');
Route::get('/v/view/{o}','VideoController@view')->where('o','[0-9]+');
Route::post('/p/delete/{o}','PhotoController@delete')->where('o','[0-9]+');
Route::post('/v/delete/{id}','VideoController@delete')->where('id','[0-9]+');
Route::post('/v/delete/{o}','VideoController@delete')->where('o','[0-9]+');
Route::post('/p/duplicates','PhotoController@duplicatesUpdate');
Route::post('/v/duplicates','VideoController@duplicatesUpdate');
Route::post('/p/deletes','PhotoController@deletesUpdate');
Route::post('/v/deletes','VideoController@deletesUpdate');
Route::post('/p/undelete/{o}','PhotoController@undelete')->where('o','[0-9]+');
Route::post('/v/undelete/{id}','VideoController@undelete')->where('id','[0-9]+');
Route::post('/v/undelete/{o}','VideoController@undelete')->where('o','[0-9]+');