2020-11-16 16:16:34 -05:00
< ? php
/**
2024-05-23 03:26:56 -04:00
* SPDX - FileCopyrightText : 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX - License - Identifier : AGPL - 3.0 - or - later
2020-11-16 16:16:34 -05:00
*/
namespace OC\Preview ;
use OC\StreamImage ;
use OCP\Files\File ;
use OCP\Http\Client\IClientService ;
use OCP\IConfig ;
use OCP\IImage ;
use OCP\Image ;
use Psr\Log\LoggerInterface ;
class Imaginary extends ProviderV2 {
/** @var IConfig */
private $config ;
/** @var IClientService */
private $service ;
/** @var LoggerInterface */
private $logger ;
public function __construct ( array $config ) {
parent :: __construct ( $config );
$this -> config = \OC :: $server -> get ( IConfig :: class );
$this -> service = \OC :: $server -> get ( IClientService :: class );
$this -> logger = \OC :: $server -> get ( LoggerInterface :: class );
}
/**
* { @ inheritDoc }
*/
public function getMimeType () : string {
return self :: supportedMimeTypes ();
}
public static function supportedMimeTypes () : string {
2024-07-13 06:59:22 -04:00
return '/(image\/(bmp|x-bitmap|png|jpeg|gif|heic|heif|svg\+xml|tiff|webp)|application\/illustrator)/' ;
2020-11-16 16:16:34 -05:00
}
public function getCroppedThumbnail ( File $file , int $maxX , int $maxY , bool $crop ) : ? IImage {
2023-04-05 06:50:08 -04:00
$maxSizeForImages = $this -> config -> getSystemValueInt ( 'preview_max_filesize_image' , 50 );
2020-11-16 16:16:34 -05:00
$size = $file -> getSize ();
if ( $maxSizeForImages !== - 1 && $size > ( $maxSizeForImages * 1024 * 1024 )) {
return null ;
}
$imaginaryUrl = $this -> config -> getSystemValueString ( 'preview_imaginary_url' , 'invalid' );
if ( $imaginaryUrl === 'invalid' ) {
$this -> logger -> error ( 'Imaginary preview provider is enabled, but no url is configured. Please provide the url of your imaginary server to the \'preview_imaginary_url\' config variable.' );
return null ;
}
$imaginaryUrl = rtrim ( $imaginaryUrl , '/' );
// Object store
$stream = $file -> fopen ( 'r' );
2023-10-25 13:47:03 -04:00
if ( ! $stream || ! is_resource ( $stream ) || feof ( $stream )) {
return null ;
}
2020-11-16 16:16:34 -05:00
$httpClient = $this -> service -> newClient ();
2023-02-05 10:44:27 -05:00
$convert = false ;
2023-03-08 11:45:12 -05:00
$autorotate = true ;
2023-02-05 10:44:27 -05:00
2020-11-16 16:16:34 -05:00
switch ( $file -> getMimeType ()) {
2023-03-08 11:45:12 -05:00
case 'image/heic' :
// Autorotate seems to be broken for Heic so disable for that
$autorotate = false ;
$mimeType = 'jpeg' ;
break ;
2020-11-16 16:16:34 -05:00
case 'image/gif' :
case 'image/png' :
$mimeType = 'png' ;
break ;
2023-02-05 10:44:27 -05:00
case 'image/svg+xml' :
case 'application/pdf' :
case 'application/illustrator' :
$convert = true ;
2023-03-08 11:45:12 -05:00
// Converted files do not need to be autorotated
$autorotate = false ;
$mimeType = 'png' ;
2023-02-05 10:44:27 -05:00
break ;
2020-11-16 16:16:34 -05:00
default :
$mimeType = 'jpeg' ;
}
2023-04-05 06:50:08 -04:00
2023-05-02 19:18:36 -04:00
$preview_format = $this -> config -> getSystemValueString ( 'preview_format' , 'jpeg' );
switch ( $preview_format ) { // Change the format to the correct one
case 'webp' :
$mimeType = 'webp' ;
break ;
default :
}
2023-03-08 11:45:12 -05:00
$operations = [];
2020-11-16 16:16:34 -05:00
2023-02-05 10:44:27 -05:00
if ( $convert ) {
2023-03-08 11:45:12 -05:00
$operations [] = [
'operation' => 'convert' ,
'params' => [
'type' => $mimeType ,
2022-04-04 10:38:38 -04:00
]
2023-02-05 10:44:27 -05:00
];
2023-03-08 11:45:12 -05:00
} elseif ( $autorotate ) {
$operations [] = [
'operation' => 'autorotate' ,
2023-02-05 10:44:27 -05:00
];
}
2020-11-16 16:16:34 -05:00
2023-05-02 19:18:36 -04:00
switch ( $mimeType ) {
case 'jpeg' :
$quality = $this -> config -> getAppValue ( 'preview' , 'jpeg_quality' , '80' );
break ;
case 'webp' :
$quality = $this -> config -> getAppValue ( 'preview' , 'webp_quality' , '80' );
break ;
default :
$quality = $this -> config -> getAppValue ( 'preview' , 'jpeg_quality' , '80' );
}
2023-03-08 11:45:12 -05:00
$operations [] = [
'operation' => ( $crop ? 'smartcrop' : 'fit' ),
'params' => [
'width' => $maxX ,
'height' => $maxY ,
'stripmeta' => 'true' ,
'type' => $mimeType ,
'norotation' => 'true' ,
'quality' => $quality ,
]
];
2020-11-16 16:16:34 -05:00
try {
2023-05-22 12:10:49 -04:00
$imaginaryKey = $this -> config -> getSystemValueString ( 'preview_imaginary_key' , '' );
2020-11-16 16:16:34 -05:00
$response = $httpClient -> post (
2022-04-04 10:38:38 -04:00
$imaginaryUrl . '/pipeline' , [
2023-05-22 12:10:49 -04:00
'query' => [ 'operations' => json_encode ( $operations ), 'key' => $imaginaryKey ],
2020-11-16 16:16:34 -05:00
'stream' => true ,
'content-type' => $file -> getMimeType (),
'body' => $stream ,
'nextcloud' => [ 'allow_local_address' => true ],
2023-03-09 07:15:35 -05:00
'timeout' => 120 ,
'connect_timeout' => 3 ,
2020-11-16 16:16:34 -05:00
]);
2023-10-27 01:54:39 -04:00
} catch ( \Throwable $e ) {
2023-08-09 10:10:27 -04:00
$this -> logger -> info ( 'Imaginary preview generation failed: ' . $e -> getMessage (), [
2020-11-16 16:16:34 -05:00
'exception' => $e ,
]);
return null ;
}
if ( $response -> getStatusCode () !== 200 ) {
2023-08-09 10:10:27 -04:00
$this -> logger -> info ( 'Imaginary preview generation failed: ' . json_decode ( $response -> getBody ())[ 'message' ]);
2020-11-16 16:16:34 -05:00
return null ;
}
2022-11-11 08:21:05 -05:00
// This is not optimal but previews are distorted if the wrong width and height values are
// used. Both dimension headers are only sent when passing the option "-return-size" to
// Imaginary.
if ( $response -> getHeader ( 'Image-Width' ) && $response -> getHeader ( 'Image-Height' )) {
$image = new StreamImage (
$response -> getBody (),
$response -> getHeader ( 'Content-Type' ),
( int ) $response -> getHeader ( 'Image-Width' ),
( int ) $response -> getHeader ( 'Image-Height' ),
);
} else {
$image = new Image ();
$image -> loadFromFileHandle ( $response -> getBody ());
2020-11-16 16:16:34 -05:00
}
return $image -> valid () ? $image : null ;
}
/**
* { @ inheritDoc }
*/
public function getThumbnail ( File $file , int $maxX , int $maxY ) : ? IImage {
return $this -> getCroppedThumbnail ( $file , $maxX , $maxY , false );
}
}