Commit b3afc98f authored by Lukas Reschke's avatar Lukas Reschke
Browse files

Merge pull request #12483 from owncloud/issue/6101-preview-providers

Issue/6101 Autoload preview providers
parents 7c96cedd ec7b55f5
......@@ -16,14 +16,6 @@ namespace OC;
use OC\Preview\Provider;
use OCP\Files\NotFoundException;
require_once 'preview/image.php';
require_once 'preview/movie.php';
require_once 'preview/mp3.php';
require_once 'preview/svg.php';
require_once 'preview/txt.php';
require_once 'preview/office.php';
require_once 'preview/bitmap.php';
class Preview {
//the thumbnail folder
const THUMBNAILS_FOLDER = 'thumbnails';
......@@ -744,10 +736,11 @@ class Preview {
return;
}
if (count(self::$providers) > 0) {
if (!empty(self::$providers)) {
return;
}
self::registerCoreProviders();
foreach (self::$registeredProviders as $provider) {
$class = $provider['class'];
$options = $provider['options'];
......@@ -759,7 +752,74 @@ class Preview {
$keys = array_map('strlen', array_keys(self::$providers));
array_multisort($keys, SORT_DESC, self::$providers);
}
protected static function registerCoreProviders() {
self::registerProvider('OC\Preview\TXT');
self::registerProvider('OC\Preview\MarkDown');
self::registerProvider('OC\Preview\Image');
self::registerProvider('OC\Preview\MP3');
// SVG, Office and Bitmap require imagick
if (extension_loaded('imagick')) {
$checkImagick = new \Imagick();
$imagickProviders = array(
'SVG' => 'OC\Preview\SVG',
'TIFF' => 'OC\Preview\TIFF',
'PDF' => 'OC\Preview\PDF',
'AI' => 'OC\Preview\Illustrator',
'PSD' => 'OC\Preview\Photoshop',
// Requires adding 'eps' => array('application/postscript', null), to lib/private/mimetypes.list.php
'EPS' => 'OC\Preview\Postscript',
);
foreach ($imagickProviders as $queryFormat => $provider) {
if (count($checkImagick->queryFormats($queryFormat)) === 1) {
self::registerProvider($provider);
}
}
if (count($checkImagick->queryFormats('PDF')) === 1) {
// Office previews are currently not supported on Windows
if (!\OC_Util::runningOnWindows() && \OC_Helper::is_function_enabled('shell_exec')) {
$officeFound = is_string(\OC::$server->getConfig()->getSystemValue('preview_libreoffice_path', null));
if (!$officeFound) {
//let's see if there is libreoffice or openoffice on this machine
$whichLibreOffice = shell_exec('command -v libreoffice');
$officeFound = !empty($whichLibreOffice);
if (!$officeFound) {
$whichOpenOffice = shell_exec('command -v openoffice');
$officeFound = !empty($whichOpenOffice);
}
}
if ($officeFound) {
self::registerProvider('OC\Preview\MSOfficeDoc');
self::registerProvider('OC\Preview\MSOffice2003');
self::registerProvider('OC\Preview\MSOffice2007');
self::registerProvider('OC\Preview\OpenDocument');
self::registerProvider('OC\Preview\StarOffice');
}
}
}
}
// Video requires avconv or ffmpeg and is therefor
// currently not supported on Windows.
if (!\OC_Util::runningOnWindows()) {
$avconvBinary = \OC_Helper::findBinaryPath('avconv');
$ffmpegBinary = ($avconvBinary) ? null : \OC_Helper::findBinaryPath('ffmpeg');
if ($avconvBinary || $ffmpegBinary) {
// FIXME // a bit hacky but didn't want to use subclasses
\OC\Preview\Movie::$avconvBinary = $avconvBinary;
\OC\Preview\Movie::$ffmpegBinary = $ffmpegBinary;
self::registerProvider('OC\Preview\Movie');
}
}
}
/**
......
......@@ -5,113 +5,33 @@
* later.
* See the COPYING-README file.
*/
namespace OC\Preview;
use Imagick;
if (extension_loaded('imagick')) {
$checkImagick = new Imagick();
class Bitmap extends Provider {
public function getMimeType() {
return null;
}
public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) {
$tmpPath = $fileview->toTmpFile($path);
//create imagick object from bitmap or vector file
try{
// Layer 0 contains either the bitmap or
// a flat representation of all vector layers
$bp = new Imagick($tmpPath . '[0]');
$bp->setImageFormat('png');
} catch (\Exception $e) {
\OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR);
return false;
}
unlink($tmpPath);
//new bitmap image object
$image = new \OC_Image($bp);
//check if image object is valid
return $image->valid() ? $image : false;
}
}
if(count($checkImagick->queryFormats('PDF')) === 1) {
//.pdf
class PDF extends Bitmap {
public function getMimeType() {
return '/application\/pdf/';
}
}
\OC\Preview::registerProvider('OC\Preview\PDF');
}
if(count($checkImagick->queryFormats('TIFF')) === 1) {
//.tiff
class TIFF extends Bitmap {
public function getMimeType() {
return '/image\/tiff/';
}
}
\OC\Preview::registerProvider('OC\Preview\TIFF');
}
if(count($checkImagick->queryFormats('AI')) === 1) {
//.ai
class Illustrator extends Bitmap {
public function getMimeType() {
return '/application\/illustrator/';
}
}
\OC\Preview::registerProvider('OC\Preview\Illustrator');
}
// Requires adding 'eps' => array('application/postscript', null), to lib/private/mimetypes.list.php
if(count($checkImagick->queryFormats('EPS')) === 1) {
//.eps
class Postscript extends Bitmap {
public function getMimeType() {
return '/application\/postscript/';
}
namespace OC\Preview;
abstract class Bitmap extends Provider {
/**
* {@inheritDoc}
*/
public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) {
$tmpPath = $fileview->toTmpFile($path);
//create imagick object from bitmap or vector file
try {
// Layer 0 contains either the bitmap or
// a flat representation of all vector layers
$bp = new \Imagick($tmpPath . '[0]');
$bp->setImageFormat('png');
} catch (\Exception $e) {
\OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR);
return false;
}
\OC\Preview::registerProvider('OC\Preview\Postscript');
}
if(count($checkImagick->queryFormats('PSD')) === 1) {
//.psd
class Photoshop extends Bitmap {
public function getMimeType() {
return '/application\/x-photoshop/';
}
unlink($tmpPath);
}
\OC\Preview::registerProvider('OC\Preview\Photoshop');
//new bitmap image object
$image = new \OC_Image($bp);
//check if image object is valid
return $image->valid() ? $image : false;
}
}
<?php
/**
* Copyright (c) 2013-2014 Georg Ehrke georg@ownCloud.com
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace OC\Preview;
//.ai
class Illustrator extends Bitmap {
/**
* {@inheritDoc}
*/
public function getMimeType() {
return '/application\/illustrator/';
}
}
......@@ -9,11 +9,16 @@
namespace OC\Preview;
class Image extends Provider {
/**
* {@inheritDoc}
*/
public function getMimeType() {
return '/image\/(?!tiff$)(?!svg.*).*/';
}
/**
* {@inheritDoc}
*/
public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) {
//get fileinfo
$fileInfo = $fileview->getFileInfo($path);
......@@ -35,5 +40,3 @@ class Image extends Provider {
}
}
\OC\Preview::registerProvider('OC\Preview\Image');
<?php
/**
* 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.
*/
namespace OC\Preview;
class MarkDown extends TXT {
/**
* {@inheritDoc}
*/
public function getMimeType() {
return '/text\/(x-)?markdown/';
}
}
......@@ -8,99 +8,87 @@
*/
namespace OC\Preview;
// movie preview is currently not supported on Windows
if (!\OC_Util::runningOnWindows()) {
$avconvBinary = \OC_Helper::findBinaryPath('avconv');
$ffmpegBinary = ($avconvBinary) ? null : \OC_Helper::findBinaryPath('ffmpeg');
if ($avconvBinary || $ffmpegBinary) {
class Movie extends Provider {
public static $avconvBinary;
public static $ffmpegBinary;
public function getMimeType() {
return '/video\/.*/';
}
public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) {
// TODO: use proc_open() and stream the source file ?
$fileInfo = $fileview->getFileInfo($path);
$useFileDirectly = (!$fileInfo->isEncrypted() && !$fileInfo->isMounted());
class Movie extends Provider {
public static $avconvBinary;
public static $ffmpegBinary;
/**
* {@inheritDoc}
*/
public function getMimeType() {
return '/video\/.*/';
}
if ($useFileDirectly) {
$absPath = $fileview->getLocalFile($path);
} else {
$absPath = \OC_Helper::tmpFile();
/**
* {@inheritDoc}
*/
public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) {
// TODO: use proc_open() and stream the source file ?
$handle = $fileview->fopen($path, 'rb');
$fileInfo = $fileview->getFileInfo($path);
$useFileDirectly = (!$fileInfo->isEncrypted() && !$fileInfo->isMounted());
// we better use 5MB (1024 * 1024 * 5 = 5242880) instead of 1MB.
// in some cases 1MB was no enough to generate thumbnail
$firstmb = stream_get_contents($handle, 5242880);
file_put_contents($absPath, $firstmb);
}
if ($useFileDirectly) {
$absPath = $fileview->getLocalFile($path);
} else {
$absPath = \OC_Helper::tmpFile();
$result = $this->generateThumbNail($maxX, $maxY, $absPath, 5);
if ($result === false) {
$result = $this->generateThumbNail($maxX, $maxY, $absPath, 1);
if ($result === false) {
$result = $this->generateThumbNail($maxX, $maxY, $absPath, 0);
}
}
$handle = $fileview->fopen($path, 'rb');
if (!$useFileDirectly) {
unlink($absPath);
}
// we better use 5MB (1024 * 1024 * 5 = 5242880) instead of 1MB.
// in some cases 1MB was no enough to generate thumbnail
$firstmb = stream_get_contents($handle, 5242880);
file_put_contents($absPath, $firstmb);
}
return $result;
$result = $this->generateThumbNail($maxX, $maxY, $absPath, 5);
if ($result === false) {
$result = $this->generateThumbNail($maxX, $maxY, $absPath, 1);
if ($result === false) {
$result = $this->generateThumbNail($maxX, $maxY, $absPath, 0);
}
}
/**
* @param int $maxX
* @param int $maxY
* @param string $absPath
* @param string $tmpPath
* @param int $second
* @return bool|\OC_Image
*/
private function generateThumbNail($maxX, $maxY, $absPath, $second)
{
$tmpPath = \OC_Helper::tmpFile();
if (self::$avconvBinary) {
$cmd = self::$avconvBinary . ' -an -y -ss ' . escapeshellarg($second) .
' -i ' . escapeshellarg($absPath) .
' -f mjpeg -vframes 1 -vsync 1 ' . escapeshellarg($tmpPath) .
' > /dev/null 2>&1';
} else {
$cmd = self::$ffmpegBinary . ' -y -ss ' . escapeshellarg($second) .
' -i ' . escapeshellarg($absPath) .
' -f mjpeg -vframes 1' .
' -s ' . escapeshellarg($maxX) . 'x' . escapeshellarg($maxY) .
' ' . escapeshellarg($tmpPath) .
' > /dev/null 2>&1';
}
if (!$useFileDirectly) {
unlink($absPath);
}
exec($cmd, $output, $returnCode);
return $result;
}
if ($returnCode === 0) {
$image = new \OC_Image();
$image->loadFromFile($tmpPath);
unlink($tmpPath);
return $image->valid() ? $image : false;
}
unlink($tmpPath);
return false;
}
/**
* @param int $maxX
* @param int $maxY
* @param string $absPath
* @param int $second
* @return bool|\OC_Image
*/
private function generateThumbNail($maxX, $maxY, $absPath, $second) {
$tmpPath = \OC_Helper::tmpFile();
if (self::$avconvBinary) {
$cmd = self::$avconvBinary . ' -an -y -ss ' . escapeshellarg($second) .
' -i ' . escapeshellarg($absPath) .
' -f mjpeg -vframes 1 -vsync 1 ' . escapeshellarg($tmpPath) .
' > /dev/null 2>&1';
} else {
$cmd = self::$ffmpegBinary . ' -y -ss ' . escapeshellarg($second) .
' -i ' . escapeshellarg($absPath) .
' -f mjpeg -vframes 1' .
' -s ' . escapeshellarg($maxX) . 'x' . escapeshellarg($maxY) .
' ' . escapeshellarg($tmpPath) .
' > /dev/null 2>&1';
}
// a bit hacky but didn't want to use subclasses
Movie::$avconvBinary = $avconvBinary;
Movie::$ffmpegBinary = $ffmpegBinary;
exec($cmd, $output, $returnCode);
\OC\Preview::registerProvider('OC\Preview\Movie');
if ($returnCode === 0) {
$image = new \OC_Image();
$image->loadFromFile($tmpPath);
unlink($tmpPath);
return $image->valid() ? $image : false;
}
unlink($tmpPath);
return false;
}
}
......@@ -8,11 +8,16 @@
namespace OC\Preview;
class MP3 extends Provider {
/**
* {@inheritDoc}
*/
public function getMimeType() {
return '/audio\/mpeg/';
}
/**
* {@inheritDoc}
*/
public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) {
$getID3 = new \getID3();
......@@ -31,6 +36,12 @@ class MP3 extends Provider {
return $this->getNoCoverThumbnail();
}
/**
* Generates a default image when the file has no cover
*
* @return false|\OC_Image False if the default image is missing or invalid,
* otherwise the image is returned as \OC_Image
*/
private function getNoCoverThumbnail() {
$icon = \OC::$SERVERROOT . '/core/img/filetypes/audio.png';
......@@ -44,5 +55,3 @@ class MP3 extends Provider {
}
}
\OC\Preview::registerProvider('OC\Preview\MP3');
<?php
/**
* 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.
*/
namespace OC\Preview;
//.docm, .dotm, .xls(m), .xlt(m), .xla(m), .ppt(m), .pot(m), .pps(m), .ppa(m)
class MSOffice2003 extends Office {
/**
* {@inheritDoc}
*/
public function getMimeType() {
return '/application\/vnd.ms-.*/';
}
}
<?php
/**
* 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.
*/
namespace OC\Preview;
//.docx, .dotx, .xlsx, .xltx, .pptx, .potx, .ppsx
class MSOffice2007 extends Office {
/**
* {@inheritDoc}
*/
public function getMimeType() {
return '/application\/vnd.openxmlformats-officedocument.*/';
}
}
<?php
/**
* 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.
*/
namespace OC\Preview;
//.doc, .dot
class MSOfficeDoc extends Office {
/**
* {@inheritDoc}
*/
public function getMimeType() {
return '/application\/msword/';
}
}
<?php
/**
* 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.
*/
namespace OC\Preview;
// office preview is currently not supported on Windows
if (!\OC_Util::runningOnWindows()) {
//we need imagick to convert
class Office extends Provider {
private $cmd;
public function getMimeType() {
return null;
}
public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) {
$this->initCmd();
if(is_null($this->cmd)) {
return false;
}
$absPath = $fileview->toTmpFile($path);
$tmpDir = get_temp_dir();
$defaultParameters = ' -env:UserInstallation=file://' . escapeshellarg($tmpDir . '/owncloud-' . \OC_Util::getInstanceId().'/') . ' --headless --nologo --nofirststartwizard --invisible --norestore --convert-to pdf --outdir ';
$clParameters = \OCP\Config::getSystemValue('preview_office_cl_parameters', $defaultParameters);
$exec = $this->cmd . $clParameters . escapeshellarg($tmpDir) . ' ' . escapeshellarg($absPath);
shell_exec($exec);
//create imagick object from pdf
try{
$pdf = new \imagick($absPath . '.pdf' . '[0]');
$pdf->setImageFormat('jpg');
} catch (\Exception $e) {
unlink($absPath);
unlink($absPath . '.pdf');
\OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR);
return false;
}
$image = new \OC_Image();
$image->loadFromData($pdf);