diff --git a/css/cwd.css b/css/cwd.css index 24d4f8460..f6d45224a 100644 --- a/css/cwd.css +++ b/css/cwd.css @@ -178,17 +178,33 @@ } /* thumbnail image */ -.elfinder-cwd-icon-image.elfinder-cwd-bgurl { +.elfinder-cwd-icon.elfinder-cwd-bgurl { background-position: center center; background-repeat: no-repeat; -moz-background-size: contain; background-size: contain; } /* thumbnail self */ -.elfinder-cwd-icon-image.elfinder-cwd-bgurl.elfinder-cwd-bgself { +.elfinder-cwd-icon.elfinder-cwd-bgurl.elfinder-cwd-bgself { -moz-background-size: cover; background-size: cover; } +/* thumbnail image video overlay */ +.elfinder-cwd-icon.elfinder-cwd-icon-video.elfinder-cwd-bgurl:after { + content: ' '; + position: relative; + display: inline-block; + top: 28px; + left: -22px; + width: 21px; + height: 28px; + background: url('../img/icons-big.png') 0 0 no-repeat; + background-position: 0 -373px; + background-size: auto !important; + opacity: .6; + filter:Alpha(Opacity=60); +} + /* "opened folder" icon on dragover */ .elfinder-cwd .elfinder-droppable-active .elfinder-cwd-icon { background-position: 0 -100px; } @@ -447,6 +463,11 @@ tr.elfinder-cwd-file td .elfinder-cwd-select { .elfinder-ltr .elfinder-cwd-view-list .elfinder-cwd-icon { left:0; } .elfinder-rtl .elfinder-cwd-view-list .elfinder-cwd-icon { right:0; } +/* thumbnail image video overlay */ +.elfinder-cwd-view-list .elfinder-cwd-icon.elfinder-cwd-icon-video.elfinder-cwd-bgurl:after { + content: none; +} + /* empty message */ .elfinder-cwd-wrapper-empty .elfinder-cwd-view-list.elfinder-cwd:after { margin-top: 0; diff --git a/php/elFinderVolumeDriver.class.php b/php/elFinderVolumeDriver.class.php index 21203b434..6452b77ad 100644 --- a/php/elFinderVolumeDriver.class.php +++ b/php/elFinderVolumeDriver.class.php @@ -108,6 +108,13 @@ abstract class elFinderVolumeDriver { **/ protected $imgLib = 'auto'; + /** + * Video to Image converter + * + * @var array + */ + protected $imgConverter = array(); + /** * Library to crypt files name * @@ -236,6 +243,14 @@ abstract class elFinderVolumeDriver { 'imgLib' => 'auto', // Fallback self image to thumbnail (nothing imgLib) 'tmbFbSelf' => true, + // Video to Image converters ['TYPE or MIME' => ['func' => function($file){ /* Converts $file to Image */ return true; }, 'maxlen' => (int)TransferLength]] + 'imgConverter' => array(), + // Max length of transfer to image converter + 'tmbVideoConvLen' => 10000000, + // Captre point seccond + 'tmbVideoConvSec' => 6, + // Resource path of fallback icon images defailt: php/resouces + 'resourcePath' => '', // Jpeg image saveing quality 'jpgQuality' => 100, // on paste file - if true - old file will be replaced with new one, if false new file get name - original_name-number.ext @@ -656,6 +671,10 @@ abstract class elFinderVolumeDriver { $this->tmbPathWritable = is_writable($path); } } + // set resouce path + if (! is_dir($this->options['resourcePath'])) { + $this->options['resourcePath'] = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'resources'; + } // set image manipulation library $type = preg_match('/^(imagick|gd|convert|auto)$/i', $this->options['imgLib']) @@ -681,6 +700,28 @@ abstract class elFinderVolumeDriver { $this->imgLib = extension_loaded('imagick')? 'imagick' : (function_exists('gd_info')? 'gd' : ''); } + // check video to img converter + if (! empty($this->options['imgConverter']) && is_array($this->options['imgConverter'])) { + foreach($this->options['imgConverter'] as $_type => $_converter) { + if (isset($_converter['func'])) { + $this->imgConverter[strtolower($_type)] = $_converter; + } + } + } + if (! isset($this->imgConverter['video'])) { + $videoLibCache = 'videoLib'; + if (($videoLibCmd = $this->session->get($videoLibCache, false)) === false) { + $videoLibCmd = ($this->procExec('ffmpeg -version') === 0)? 'ffmpeg' : ''; + $this->session->set($videoLibCache, $videoLibCmd); + } + if ($videoLibCmd) { + $this->imgConverter['video'] = array( + 'func' => array($this, $videoLibCmd . 'ToImg'), + 'maxlen' => $this->options['tmbVideoConvLen'] + ); + } + } + // check archivers if (empty($this->archivers['create'])) { $this->disabled[] ='archive'; @@ -1604,7 +1645,18 @@ abstract class elFinderVolumeDriver { $stat = $this->stat($path); if (isset($stat['tmb'])) { - return $stat['tmb'] == "1" ? $this->createTmb($path, $stat) : $stat['tmb']; + $res = $stat['tmb'] == "1" ? $this->createTmb($path, $stat) : $stat['tmb']; + if (! $res) { + list($type) = explode('/', $stat['mime']); + $fallback = $this->options['resourcePath'] . DIRECTORY_SEPARATOR . strtolower($type) . '.png'; + if (is_file($fallback)) { + $res = $this->tmbname($stat); + if (! copy($fallback, $this->tmbPath . DIRECTORY_SEPARATOR . $res)) { + $res = false; + } + } + } + return $res; } return false; } @@ -2625,6 +2677,34 @@ abstract class elFinderVolumeDriver { return false; } + /** + * Convert Video To Image by ffmpeg + * + * @param $file video source file path + * @param $stat file stat array + * @return bool + * @author Naoki Sawada + */ + public function ffmpegToImg($file, $stat) { + $name = basename($file); + $path = dirname($file); + $tmp = $path . DIRECTORY_SEPARATOR . md5($name); + $GLOBALS['elFinderTempFiles'][] = $tmp; // regist to remove at the end + if (rename($file, $tmp)) { + // specific start time by file name (xxx^[sec].[extention] - video^3.mp4) + if (preg_match('/\^(\d+(?:\.\d+)?)\.[^.]+$/', $stat['name'], $_m)) { + $ss = $_m[1]; + } else { + $ss = $this->options['tmbVideoConvSec']; + } + $cmd = sprintf('ffmpeg -ss 00:00:%.3f -vframes 1 -i %s -f image2 %s', $ss, escapeshellarg($tmp), escapeshellarg($file)); + $r = $this->procExec($cmd); + unlink($tmp); + return ($r === 0); + } + return false; + } + /** * Save error message * @@ -4126,11 +4206,24 @@ abstract class elFinderVolumeDriver { * @author Dmitry (dio) Levashov **/ protected function canCreateTmb($path, $stat, $checkTmbPath = true) { - return (!$checkTmbPath || $this->tmbPathWritable) - && (!$this->tmbPath || strpos($path, $this->tmbPath) === false) // do not create thumnbnail for thumnbnail - && $this->imgLib - && strpos($stat['mime'], 'image') === 0 - && ($this->imgLib == 'gd' ? in_array($stat['mime'], array('image/jpeg', 'image/png', 'image/gif', 'image/x-ms-bmp')) : true); + if ((! $checkTmbPath || $this->tmbPathWritable) + && (! $this->tmbPath || strpos($path, $this->tmbPath) === false) // do not create thumnbnail for thumnbnail + ) { + $mime = strtolower($stat['mime']); + list($type) = explode('/', $mime); + if (! empty($this->imgConverter)) { + if (isset($this->imgConverter[$mime])) { + return true; + } + if (isset($this->imgConverter[$type])) { + return true; + } + } + return $this->imgLib + && ($type === 'image') + && ($this->imgLib == 'gd' ? in_array($stat['mime'], array('image/jpeg', 'image/png', 'image/gif', 'image/x-ms-bmp')) : true); + } + return false; } /** @@ -4163,6 +4256,27 @@ abstract class elFinderVolumeDriver { $name = $this->tmbname($stat); $tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$name; + $maxlength = -1; + $imgConverter = null; + + // check imgConverter + $mime = strtolower($stat['mime']); + list($type) = explode('/', $mime); + if (isset($this->imgConverter[$mime])) { + $imgConverter = $this->imgConverter[$mime]['func']; + if (! empty($this->imgConverter[$mime]['maxlen'])) { + $maxlength = intval($this->imgConverter[$mime]['maxlen']); + } + } else if (isset($this->imgConverter[$type])) { + $imgConverter = $this->imgConverter[$type]['func']; + if (! empty($this->imgConverter[$type]['maxlen'])) { + $maxlength = intval($this->imgConverter[$type]['maxlen']); + } + } + if ($imgConverter && ! is_callable($imgConverter)) { + return false; + } + // copy image into tmbPath so some drivers does not store files on local fs if (($src = $this->fopenCE($path, 'rb')) == false) { return false; @@ -4173,13 +4287,19 @@ abstract class elFinderVolumeDriver { return false; } - while (!feof($src)) { - fwrite($trg, fread($src, 8192)); - } + stream_copy_to_stream($src, $trg, $maxlength); $this->fcloseCE($src, $path); fclose($trg); + // call imgConverter + if ($imgConverter) { + if (! call_user_func_array($imgConverter, array($tmb, $stat, $this))) { + file_exists($tmb) && unlink($tmb); + return false; + } + } + $result = false; $tmbSize = $this->tmbSize; @@ -4213,7 +4333,7 @@ abstract class elFinderVolumeDriver { } catch (Exception $e) {} } if (! $result) { - unlink($tmb); + file_exists($tmb) && unlink($tmb); return false; } $result = false;