|
|
|
@ -1,28 +1,139 @@ |
|
|
|
|
<?php |
|
|
|
|
/** |
|
|
|
|
* Copyright (c) 2012 Frank Karlitschek frank@owncloud.org |
|
|
|
|
* Copyright (c) 2013 Frank Karlitschek frank@owncloud.org |
|
|
|
|
* Copyright (c) 2013 Georg Ehrke georg@ownCloud.com |
|
|
|
|
* This file is licensed under the Affero General Public License version 3 or |
|
|
|
|
* later. |
|
|
|
|
* See the COPYING-README file. |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
TODO: |
|
|
|
|
- delete thumbnails if files change. |
|
|
|
|
- make it work with external filesystem files. |
|
|
|
|
- movies support |
|
|
|
|
- pdf support |
|
|
|
|
- mp3/id3 support |
|
|
|
|
- more file types |
|
|
|
|
* Thumbnails: |
|
|
|
|
* structure of filename: |
|
|
|
|
* /data/user/thumbnails/pathhash/x-y.png |
|
|
|
|
* |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
|
class OC_Preview { |
|
|
|
|
//the thumbnail folder |
|
|
|
|
const THUMBNAILS_FOLDER = 'thumbnails'; |
|
|
|
|
const MAX_SCALE_FACTOR = 2; |
|
|
|
|
|
|
|
|
|
//fileview object |
|
|
|
|
static private $fileview = null; |
|
|
|
|
|
|
|
|
|
//preview providers |
|
|
|
|
static private $providers = array(); |
|
|
|
|
static private $registeredProviders = array(); |
|
|
|
|
|
|
|
|
|
class OC_Preview { |
|
|
|
|
/** |
|
|
|
|
* @brief check if thumbnail or bigger version of thumbnail of file is cached |
|
|
|
|
* @param $file The path to the file where you want a thumbnail from |
|
|
|
|
* @param $maxX The maximum X size of the thumbnail. It can be smaller depending on the shape of the image |
|
|
|
|
* @param $maxY The maximum Y size of the thumbnail. It can be smaller depending on the shape of the image |
|
|
|
|
* @return mixed (bool / string) |
|
|
|
|
* false if thumbnail does not exist |
|
|
|
|
* path to thumbnail if thumbnail exists |
|
|
|
|
*/ |
|
|
|
|
private static function isCached($file, $maxX, $maxY, $scalingup){ |
|
|
|
|
$fileinfo = self::$fileview->getFileInfo($file); |
|
|
|
|
$fileid = self::$fileinfo['fileid']; |
|
|
|
|
|
|
|
|
|
//echo self::THUMBNAILS_FOLDER . PATH_SEPARATOR . $fileid; |
|
|
|
|
if(!self::$fileview->is_dir(self::THUMBNAILS_FOLDER . PATH_SEPARATOR . $fileid)){ |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//does a preview with the wanted height and width already exist? |
|
|
|
|
if(self::$fileview->file_exists(self::THUMBNAILS_FOLDER . PATH_SEPARATOR . $fileid . PATH_SEPARATOR . $x . '-' . $y . '.png')){ |
|
|
|
|
return self::THUMBNAILS_FOLDER . PATH_SEPARATOR . $fileid . PATH_SEPARATOR . $x . '-' . $y . '.png'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$wantedaspectratio = $maxX / $maxY; |
|
|
|
|
|
|
|
|
|
//array for usable cached thumbnails |
|
|
|
|
$possiblethumbnails = array(); |
|
|
|
|
|
|
|
|
|
$allthumbnails = self::$fileview->getDirectoryContent(self::THUMBNAILS_FOLDER . PATH_SEPARATOR . $fileid); |
|
|
|
|
foreach($allthumbnails as $thumbnail){ |
|
|
|
|
$size = explode('-', $thumbnail['name']); |
|
|
|
|
$x = $size[0]; |
|
|
|
|
$y = $size[1]; |
|
|
|
|
|
|
|
|
|
$aspectratio = $x / $y; |
|
|
|
|
if($aspectratio != $wantedaspectratio){ |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if($x < $maxX || $y < $maxY){ |
|
|
|
|
if($scalingup){ |
|
|
|
|
$scalefactor = $maxX / $x; |
|
|
|
|
if($scalefactor > self::MAX_SCALE_FACTOR){ |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
}else{ |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$possiblethumbnails[$x] = $thumbnail['path']; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(count($possiblethumbnails) === 0){ |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(count($possiblethumbnails) === 1){ |
|
|
|
|
return current($possiblethumbnails); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ksort($possiblethumbnails); |
|
|
|
|
|
|
|
|
|
if(key(reset($possiblethumbnails)) > $maxX){ |
|
|
|
|
return current(reset($possiblethumbnails)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(key(end($possiblethumbnails)) < $maxX){ |
|
|
|
|
return current(end($possiblethumbnails)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
foreach($possiblethumbnails as $width => $path){ |
|
|
|
|
if($width < $maxX){ |
|
|
|
|
continue; |
|
|
|
|
}else{ |
|
|
|
|
return $path; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// the thumbnail cache folder |
|
|
|
|
const THUMBNAILS_FOLDER = 'thumbnails'; |
|
|
|
|
/** |
|
|
|
|
* @brief delete a preview with a specfic height and width |
|
|
|
|
* @param $file path to the file |
|
|
|
|
* @param $x width of preview |
|
|
|
|
* @param $y height of preview |
|
|
|
|
* @return image |
|
|
|
|
*/ |
|
|
|
|
public static function deletePreview($file, $x, $y){ |
|
|
|
|
self::init(); |
|
|
|
|
|
|
|
|
|
$fileinfo = self::$fileview->getFileInfo($file); |
|
|
|
|
$fileid = self::$fileinfo['fileid']; |
|
|
|
|
|
|
|
|
|
return self::$fileview->unlink(self::THUMBNAILS_FOLDER . PATH_SEPARATOR . $fileid . PATH_SEPARATOR . $x . '-' . $y . '.png'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @brief deletes all previews of a file |
|
|
|
|
* @param $file path of file |
|
|
|
|
* @return bool |
|
|
|
|
*/ |
|
|
|
|
public static function deleteAllPrevies($file){ |
|
|
|
|
self::init(); |
|
|
|
|
|
|
|
|
|
$fileinfo = self::$fileview->getFileInfo($file); |
|
|
|
|
$fileid = self::$fileinfo['fileid']; |
|
|
|
|
|
|
|
|
|
return self::$fielview->rmdir(self::THUMBNAILS_FOLDER . PATH_SEPARATOR . $fileid); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @brief return a preview of a file |
|
|
|
@ -32,146 +143,115 @@ class OC_Preview { |
|
|
|
|
* @param $scaleup Scale smaller images up to the thumbnail size or not. Might look ugly |
|
|
|
|
* @return image |
|
|
|
|
*/ |
|
|
|
|
static public function show($file,$maxX,$maxY,$scalingup) { |
|
|
|
|
// get the mimetype of the file |
|
|
|
|
$mimetype=explode('/',OC_FileSystem::getMimeType($file)); |
|
|
|
|
// it´s an image |
|
|
|
|
if($mimetype[0]=='image'){ |
|
|
|
|
OCP\Response::enableCaching(3600 * 24); // 24 hour |
|
|
|
|
$image=OC_PreviewImage::getThumbnail($file,$maxX,$maxY,$scalingup); |
|
|
|
|
$image->show(); |
|
|
|
|
|
|
|
|
|
// it´s a video |
|
|
|
|
}elseif($mimetype[0]=='video'){ |
|
|
|
|
OCP\Response::enableCaching(3600 * 24); // 24 hour |
|
|
|
|
OC_PreviewMovie::getThumbnail($file,$maxX,$maxY,$scalingup); |
|
|
|
|
|
|
|
|
|
// it´s something else. Let´s create a dummy preview |
|
|
|
|
}else{ |
|
|
|
|
header('Content-type: image/png'); |
|
|
|
|
OC_PreviewUnknown::getThumbnail($maxX,$maxY); |
|
|
|
|
public static function getPreview($file, $maxX, $maxY, $scalingup){ |
|
|
|
|
self::init(); |
|
|
|
|
|
|
|
|
|
$cached = self::isCached($file, $maxX, $maxY); |
|
|
|
|
if($cached){ |
|
|
|
|
$image = new \OC_Image($cached); |
|
|
|
|
if($image->width() != $maxX && $image->height != $maxY){ |
|
|
|
|
$image->preciseResize($maxX, $maxY); |
|
|
|
|
} |
|
|
|
|
return $image; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$mimetype = self::$fileview->getMimeType($file); |
|
|
|
|
|
|
|
|
|
$preview; |
|
|
|
|
|
|
|
|
|
foreach(self::$providers as $supportedmimetype => $provider){ |
|
|
|
|
if(!preg_match($supportedmimetype, $mimetype)){ |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$preview = $provider->getThumbnail($file, $maxX, $maxY, $scalingup); |
|
|
|
|
|
|
|
|
|
if(!$preview){ |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(!($preview instanceof \OC_Image)){ |
|
|
|
|
$preview = @new \OC_Image($preview); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//cache thumbnail |
|
|
|
|
$preview->save(self::$filesview->getAbsolutePath(self::THUMBNAILS_FOLDER . PATH_SEPARATOR . $fileid . PATH_SEPARATOR . $x . '-' . $y . '.png')); |
|
|
|
|
|
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return $preview; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @brief return a preview of a file |
|
|
|
|
* @param $file The path to the file where you want a thumbnail from |
|
|
|
|
* @param $maxX The maximum X size of the thumbnail. It can be smaller depending on the shape of the image |
|
|
|
|
* @param $maxY The maximum Y size of the thumbnail. It can be smaller depending on the shape of the image |
|
|
|
|
* @param $scaleup Scale smaller images up to the thumbnail size or not. Might look ugly |
|
|
|
|
* @return image |
|
|
|
|
*/ |
|
|
|
|
public static function showPreview($file, $maxX, $maxY, $scalingup = true, $fontsize = 12){ |
|
|
|
|
OCP\Response::enableCaching(3600 * 24); // 24 hour |
|
|
|
|
$preview = self::getPreview($file, $maxX, $maxY, $scalingup, $fontsize); |
|
|
|
|
$preview->show(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @brief check whether or not providers and views are initialized and initialize if not |
|
|
|
|
* @return void |
|
|
|
|
*/ |
|
|
|
|
private static function init(){ |
|
|
|
|
if(empty(self::$providers)){ |
|
|
|
|
self::initProviders(); |
|
|
|
|
} |
|
|
|
|
if(is_null(self::$thumbnailsview) || is_null(self::$userlandview)){ |
|
|
|
|
self::initViews(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OC_PreviewImage { |
|
|
|
|
|
|
|
|
|
public static function getThumbnail($path,$maxX,$maxY,$scalingup) { |
|
|
|
|
|
|
|
|
|
$thumbnails_view = new \OC_FilesystemView('/'.\OCP\User::getUser() .'/'.OC_Preview::THUMBNAILS_FOLDER); |
|
|
|
|
|
|
|
|
|
// is a preview already in the cache? |
|
|
|
|
if ($thumbnails_view->file_exists($path.'-'.$maxX.'-'.$maxY.'-'.$scalingup)) { |
|
|
|
|
return new \OC_Image($thumbnails_view->getLocalFile($path.'-'.$maxX.'-'.$maxY.'-'.$scalingup)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// does the sourcefile exist? |
|
|
|
|
if (!\OC_Filesystem::file_exists($path)) { |
|
|
|
|
\OC_Log::write('Preview', 'File '.$path.' don\'t exists', \OC_Log::WARN); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// open the source image |
|
|
|
|
$image = new \OC_Image(); |
|
|
|
|
$image->loadFromFile(\OC_Filesystem::getLocalFile($path)); |
|
|
|
|
if (!$image->valid()) return false; |
|
|
|
|
|
|
|
|
|
// fix the orientation |
|
|
|
|
$image->fixOrientation(); |
|
|
|
|
|
|
|
|
|
// calculate the right preview size |
|
|
|
|
$Xsize=$image->width(); |
|
|
|
|
$Ysize=$image->height(); |
|
|
|
|
if (($Xsize/$Ysize)>($maxX/$maxY)) { |
|
|
|
|
$factor=$maxX/$Xsize; |
|
|
|
|
} else { |
|
|
|
|
$factor=$maxY/$Ysize; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// only scale up if requested |
|
|
|
|
if($scalingup==false) { |
|
|
|
|
if($factor>1) $factor=1; |
|
|
|
|
} |
|
|
|
|
$newXsize=$Xsize*$factor; |
|
|
|
|
$newYsize=$Ysize*$factor; |
|
|
|
|
|
|
|
|
|
// resize |
|
|
|
|
$ret = $image->preciseResize($newXsize, $newYsize); |
|
|
|
|
if (!$ret) { |
|
|
|
|
\OC_Log::write('Preview', 'Couldn\'t resize image', \OC_Log::ERROR); |
|
|
|
|
unset($image); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// store in cache |
|
|
|
|
$l = $thumbnails_view->getLocalFile($path.'-'.$maxX.'-'.$maxY.'-'.$scalingup); |
|
|
|
|
$image->save($l); |
|
|
|
|
|
|
|
|
|
return $image; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OC_PreviewMovie { |
|
|
|
|
|
|
|
|
|
public static function isAvailable() { |
|
|
|
|
$check=shell_exec('ffmpeg'); |
|
|
|
|
if($check==NULL) return(false); else return(true); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static function getThumbnail($path,$maxX,$maxY,$scalingup) { |
|
|
|
|
$thumbnails_view = new \OC_FilesystemView('/'.\OCP\User::getUser() .'/'.OC_Preview::THUMBNAILS_FOLDER); |
|
|
|
|
|
|
|
|
|
// is a preview already in the cache? |
|
|
|
|
if ($thumbnails_view->file_exists($path.'-'.$maxX.'-'.$maxY.'-'.$scalingup)) { |
|
|
|
|
return new \OC_Image($thumbnails_view->getLocalFile($path.'-'.$maxX.'-'.$maxY.'-'.$scalingup)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// does the sourcefile exist? |
|
|
|
|
if (!\OC_Filesystem::file_exists($path)) { |
|
|
|
|
\OC_Log::write('Preview', 'File '.$path.' don\'t exists', \OC_Log::WARN); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// call ffmpeg to do the screenshot |
|
|
|
|
shell_exec('ffmpeg -y -i {'.escapeshellarg($path).'} -f mjpeg -vframes 1 -ss 1 -s {'.escapeshellarg($maxX).'}x{'.escapeshellarg($maxY).'} {.'.$thumbnails_view->getLocalFile($path.'-'.$maxX.'-'.$maxY.'-'.$scalingup).'}'); |
|
|
|
|
|
|
|
|
|
// output the generated Preview |
|
|
|
|
$thumbnails_view->getLocalFile($path.'-'.$maxX.'-'.$maxY.'-'.$scalingup); |
|
|
|
|
unset($thumbnails_view); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @brief register a new preview provider to be used |
|
|
|
|
* @param string $provider class name of a OC_Preview_Provider |
|
|
|
|
* @return void |
|
|
|
|
*/ |
|
|
|
|
public static function registerProvider($class, $options=array()){ |
|
|
|
|
self::$registeredProviders[]=array('class'=>$class, 'options'=>$options); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class OC_PreviewUnknown { |
|
|
|
|
public static function getThumbnail($maxX,$maxY) { |
|
|
|
|
/** |
|
|
|
|
* @brief create instances of all the registered preview providers |
|
|
|
|
* @return void |
|
|
|
|
*/ |
|
|
|
|
private static function initProviders(){ |
|
|
|
|
if(count(self::$providers)>0) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
foreach(self::$registeredProviders as $provider) { |
|
|
|
|
$class=$provider['class']; |
|
|
|
|
$options=$provider['options']; |
|
|
|
|
|
|
|
|
|
// check if GD is installed |
|
|
|
|
if(!extension_loaded('gd') || !function_exists('gd_info')) { |
|
|
|
|
OC_Log::write('preview', __METHOD__.'(): GD module not installed', OC_Log::ERROR); |
|
|
|
|
return false; |
|
|
|
|
$object = new $class($options); |
|
|
|
|
|
|
|
|
|
self::$providers[$object->getMimeType()] = $object; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// create a white image |
|
|
|
|
$image = imagecreatetruecolor($maxX, $maxY); |
|
|
|
|
$color = imagecolorallocate($image, 255, 255, 255); |
|
|
|
|
imagefill($image, 0, 0, $color); |
|
|
|
|
|
|
|
|
|
// output the image |
|
|
|
|
imagepng($image); |
|
|
|
|
imagedestroy($image); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$keys = array_map('strlen', array_keys(self::$providers)); |
|
|
|
|
array_multisort($keys, SORT_DESC, self::$providers); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @brief initialize a new \OC\Files\View object |
|
|
|
|
* @return void |
|
|
|
|
*/ |
|
|
|
|
private static function initViews(){ |
|
|
|
|
if(is_null(self::$fileview)){ |
|
|
|
|
self::$fileview = new OC\Files\View(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static function previewRouter($params){ |
|
|
|
|
var_dump($params); |
|
|
|
|
} |
|
|
|
|
} |