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

View File

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

View File

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

View File

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

View File

@ -2,28 +2,26 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use Illuminate\Http\Response;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Http\Requests; use App\Models\Video;
use App\Model\Video;
use App\Jobs\VideoDelete; use App\Jobs\VideoDelete;
use App\Helpers\VideoStream; use App\Helpers\VideoStream;
class VideoController extends Controller class VideoController extends Controller
{ {
/** /**
* Create a new controller instance. * Create a new controller instance.
* *
* @return void * @return void
*/ */
public function __construct() public function __construct()
{ {
$this->middleware('guest'); $this->middleware('guest');
} }
public function delete($id) public function delete($id)
{ {
$po = Video::notRemove()->findOrFail($id); $po = Video::notRemove()->findOrFail($id);
if ($po) if ($po)
@ -33,15 +31,15 @@ class VideoController extends Controller
} }
return redirect()->action('VideoController@info',[$id]); 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)]); 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) foreach ($request->input('remove') as $k=>$id)
{ {
$o = Video::findOrFail($k); $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); 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)]); 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) foreach ($request->input('items') as $id)
{ {
$po = Video::findOrFail($id); $po = Video::findOrFail($id);
@ -77,14 +75,15 @@ class VideoController extends Controller
} }
return redirect()->action('VideoController@duplicates','?page='.$request->input('page')); 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); $po = Video::findOrFail($id);
if ($po) if ($po)
@ -94,10 +93,10 @@ class VideoController extends Controller
} }
return redirect()->action('VideoController@info',[$id]); return redirect()->action('VideoController@info',[$id]);
} }
public function view($id) public function view($id)
{ {
(new VideoStream(Video::findOrFail($id)->file_path()))->start(); (new VideoStream(Video::findOrFail($id)->file_path()))->start();
} }
} }

View File

@ -2,6 +2,7 @@
namespace App\Models\Abstracted; namespace App\Models\Abstracted;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use DB; use DB;
@ -108,7 +109,7 @@ abstract class Catalog extends Model
*/ */
public function date_taken(): string 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 public function device(): string
@ -163,7 +164,7 @@ abstract class Catalog extends Model
if ($new) if ($new)
$file = sprintf('%s.%s',((is_null($this->date_created) OR ! $this->date_created) $file = sprintf('%s.%s',((is_null($this->date_created) OR ! $this->date_created)
? sprintf('UNKNOWN/%07s',$this->file_path_id()) ? 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; 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); return $this->HTMLCheckbox('flag',$this->id,$this->flag);
} }
public function getDateCreatedAttribute() {
return Carbon::createFromTimestamp($this->attributes['date_created']);
}
/** /**
* @deprecated * @deprecated
*/ */
@ -337,6 +342,8 @@ abstract class Catalog extends Model
->first(); ->first();
} }
abstract public function property(string $property);
/** /**
* Return my class shortname * Return my class shortname
*/ */
@ -368,13 +375,12 @@ abstract class Catalog extends Model
*/ */
public function signature($short=FALSE) 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 * Trim a string
* *
* @todo rename stringtrim
* @param string $string * @param string $string
* @param int $chrs * @param int $chrs
* @return string * @return string

View File

@ -2,7 +2,6 @@
namespace App\Models; namespace App\Models;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
class Photo extends Abstracted\Catalog class Photo extends Abstracted\Catalog
@ -60,7 +59,7 @@ class Photo extends Abstracted\Catalog
public function getHtmlImageURL(): string 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(); return $imo->getImageBlob();
} }
public function property($property) public function property(string $property)
{ {
if (! $this->o()) if (! $this->o())
return NULL; return NULL;
@ -175,10 +174,6 @@ class Photo extends Abstracted\Catalog
return $this->o() ? $this->_o->getImageProperties() : []; return $this->o() ? $this->_o->getImageProperties() : [];
} }
public function getDateCreatedAttribute() {
return Carbon::createFromTimestamp($this->attributes['date_created']);
}
public function setDateCreated() public function setDateCreated()
{ {
$this->date_created = $this->property('creationdate'); $this->date_created = $this->property('creationdate');

View File

@ -3,16 +3,22 @@
namespace App\Models; namespace App\Models;
use DB; use DB;
use Illuminate\Support\Arr;
class Video extends Abstracted\Catalog class Video extends Abstracted\Catalog
{ {
// ID3 Object // ID3 Object
private $_o = NULL; private $_o = NULL;
public function getIDLinkAttribute() public function getIDLinkAttribute()
{ {
return $this->HTMLLinkAttribute($this->id,url('v/info').'/'); 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 * 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); 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()) if (! $this->o())
return NULL; return NULL;
switch ($property) switch ($property) {
{
case 'creationdate': case 'creationdate':
// Try creation_Data // 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 // Try creation_Data
if (is_null($x)) 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; return $x;
case 'make': return array_get($this->_o->info,'quicktime.comments.make.0'); case 'make': return Arr::get($this->_o->info,'quicktime.comments.make.0');
case 'model': return array_get($this->_o->info,'quicktime.comments.model.0'); case 'model': return Arr::get($this->_o->info,'quicktime.comments.model.0');
case 'software': return array_get($this->_o->info,'quicktime.comments.software.0'); case 'software': return Arr::get($this->_o->info,'quicktime.comments.software.0');
case 'signature': return array_get($this->_o->info,'sha1_data'); case 'signature': return Arr::get($this->_o->info,'sha1_data');
#case 'height': return $this->subatomsearch('quicktime.moov.subatoms',['trak','tkhd'],'height'); break; #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 'width': return $this->subatomsearch('quicktime.moov.subatoms',['trak','tkhd'],'width'); break;
case 'height': return array_get($this->_o->info,'video.resolution_y'); case 'height': return Arr::get($this->_o->info,'video.resolution_y');
case 'width': return array_get($this->_o->info,'video.resolution_x'); case 'width': return Arr::get($this->_o->info,'video.resolution_x');
case 'length': return array_get($this->_o->info,'playtime_seconds'); case 'length': return Arr::get($this->_o->info,'playtime_seconds');
case 'type': return array_get($this->_o->info,'video.dataformat'); case 'type': return Arr::get($this->_o->info,'video.dataformat');
case 'codec': return array_get($this->_o->info,'audio.codec'); case 'codec': return Arr::get($this->_o->info,'audio.codec');
case 'audiochannels': return array_get($this->_o->info,'audio.channels'); case 'audiochannels': return Arr::get($this->_o->info,'audio.channels');
case 'samplerate': return array_get($this->_o->info,'audio.sample_rate'); case 'samplerate': return Arr::get($this->_o->info,'audio.sample_rate');
case 'channelmode': return array_get($this->_o->info,'audio.channelmode'); case 'channelmode': return Arr::get($this->_o->info,'audio.channelmode');
case 'gps_lat': return array_get($this->_o->info,'quicktime.comments.gps_latitude.0'); case 'gps_lat': return Arr::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_lon': return Arr::get($this->_o->info,'quicktime.comments.gps_longitude.0');
case 'gps_altitude': return array_get($this->_o->info,'quicktime.comments.gps_altitude.0'); case 'gps_altitude': return Arr::get($this->_o->info,'quicktime.comments.gps_altitude.0');
case 'identifier': if ($x=array_get($this->_o->info,'quicktime.comments')) { case 'identifier':
return array_get(array_flatten(array_only($x,'content.identifier')),0); if ($x=Arr::get($this->_o->info,'quicktime.comments')) {
return Arr::get(Arr::flatten(Arr::only($x,'content.identifier')),0);
} }
break; break;
default: default:
return NULL; return NULL;
} }
@ -85,24 +92,23 @@ class Video extends Abstracted\Catalog
* Navigate through ID3 data to find the value. * Navigate through ID3 data to find the value.
*/ */
private function subatomsearch($atom,array $paths,$key,array $data=[]) { 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. // Didnt find it.
return NULL; return NULL;
} }
foreach ($paths as $path) foreach ($paths as $path) {
{
$found = FALSE; $found = FALSE;
foreach ($data as $array)
{ foreach ($data as $array) {
if ($array['name'] === $path) if ($array['name'] === $path) {
{
$found = TRUE; $found = TRUE;
if ($path != last($paths)) if ($path != last($paths))
$data = $array['subatoms']; $data = $array['subatoms'];
else else
$data = $array; $data = $array;
break; 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)); 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", "php": "^7.2",
"barryvdh/laravel-debugbar": "^3.2", "barryvdh/laravel-debugbar": "^3.2",
"fideloper/proxy": "^4.0", "fideloper/proxy": "^4.0",
"james-heinrich/getid3": "^1.9",
"laravel/framework": "^6.4", "laravel/framework": "^6.4",
"laravel/socialite": "^4.2", "laravel/socialite": "^4.2",
"laravel/tinker": "^1.0", "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", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "99533013200b14683fe672984d68b218", "content-hash": "680a81e5f8c3bb384cad5608775023d6",
"packages": [ "packages": [
{ {
"name": "barryvdh/laravel-debugbar", "name": "barryvdh/laravel-debugbar",
@ -839,6 +839,69 @@
"description": "Highlight PHP code in terminal", "description": "Highlight PHP code in terminal",
"time": "2018-09-29T18:48:56+00:00" "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", "name": "laravel/framework",
"version": "v6.5.0", "version": "v6.5.0",

View File

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

View File

@ -18,6 +18,7 @@
<span class="badge badge-warning right">{{ \App\Models\Photo::where('duplicate',TRUE)->count() }}</span> <span class="badge badge-warning right">{{ \App\Models\Photo::where('duplicate',TRUE)->count() }}</span>
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a href="{{ url('p/deletes') }}" class="nav-link @if(preg_match('#^p/deletes$/'.'#',request()->path())) active @endif"> <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> <i class="fa fa-calendar nav-icon"></i> <p>Delete</p>
@ -25,4 +26,26 @@
</a> </a>
</li> </li>
</ul> </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> </li>

View File

@ -1,90 +1,116 @@
@extends('layouts.app') @extends('adminlte::layouts.app')
@section('content') @section('htmlheader_title')
<div class="container"> 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="row">
<div class="col-md-10 col-md-offset-1"> <div class="col-4">
<div class="panel panel-default"> <a href="{{ url('p/view',$o->id) }}">{!! $o->getHtmlImageURL() !!}</a>
<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="panel-body"> <span class="pagination justify-content-center">
<div class="col-md-4"> <nav>
<div class="text-center"> <ul class="pagination">
{!! $video->view() !!} <li class="page-item @if(! $x=$o->previous())disabled @endif" aria-disabled="@if(! $x)true @else false @endif" aria-label="&laquo; Previous">
<ul class="pagination"> <a class="page-link" href="{{ $x ? url('p/info',$x->id) : '#' }}">&lt;&lt;</a>
<li <?php if (! $x = $video->previous()) : ?>class="disabled"<?php endif ?>><a href="{{ $x ? url('/v/info/'.$x->id) : '#' }}">&lt;&lt;</a></li> </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>
</div> <li class="page-item active" aria-current="page"><span class="page-link">{{ $o->id }}</span></li>
<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>
<?php if ($video->remove) : ?> <li class="page-item @if(! $x=$o->next())disabled @endif" aria-disabled="@if(! $x)true @else false @endif" aria-label="&laquo; Previous">
<form action="{{ url('/v/undelete/'.$video->id) }}" method="POST"> <a class="page-link" href="{{ $x ? url('p/info',$x->id) : '#' }}">&gt;&gt;</a>
<button class="btn btn-default">Undelete</button> </li>
<?php else : ?> </ul>
<form action="{{ url('/v/delete/'.$video->id) }}" method="POST"> </nav>
<button class="btn btn-default">Delete</button> </span>
<?php endif ?> </div>
{{ csrf_field() }}
</form> <div class="col-8">
</div> <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> </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> </div>
</div>
@endsection @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('/v/duplicates/{id?}','VideoController@duplicates')->where('id','[0-9]+');
Route::get('/p/info/{o}','PhotoController@info')->where('o','[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/thumbnail/{o}','PhotoController@thumbnail')->where('o','[0-9]+');
Route::get('/p/view/{o}','PhotoController@view')->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('/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('/p/duplicates','PhotoController@duplicatesUpdate');
Route::post('/v/duplicates','VideoController@duplicatesUpdate'); Route::post('/v/duplicates','VideoController@duplicatesUpdate');
Route::post('/p/deletes','PhotoController@deletesUpdate'); Route::post('/p/deletes','PhotoController@deletesUpdate');
Route::post('/v/deletes','VideoController@deletesUpdate'); Route::post('/v/deletes','VideoController@deletesUpdate');
Route::post('/p/undelete/{o}','PhotoController@undelete')->where('o','[0-9]+'); 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]+');