Renamed graphics extension to media extension

This commit is contained in:
Matthew Barbour 2019-06-24 10:05:16 -05:00 committed by matthew
parent 0c16d3e78c
commit a41e99d1af
17 changed files with 229 additions and 137 deletions

View File

@ -95,7 +95,6 @@ function get_extension_from_mime(String $file_path): String
throw new UploadException("Could not determine file mime type: ".$file_path); throw new UploadException("Could not determine file mime type: ".$file_path);
} }
/** /**
* Given a full size pair of dimensions, return a pair scaled down to fit * Given a full size pair of dimensions, return a pair scaled down to fit
* into the configured thumbnail square, with ratio intact. * into the configured thumbnail square, with ratio intact.
@ -179,7 +178,7 @@ function create_image_thumb(string $hash, string $type, string $engine = null) {
$engine = $config->get_string(ImageConfig::THUMB_ENGINE); $engine = $config->get_string(ImageConfig::THUMB_ENGINE);
} }
send_event(new GraphicResizeEvent( send_event(new MediaResizeEvent(
$engine, $engine,
$inname, $inname,
$type, $type,

View File

@ -40,7 +40,7 @@ _d("SEARCH_ACCEL", false); // boolean use search accelerator
_d("WH_SPLITS", 1); // int how many levels of subfolders to put in the warehouse _d("WH_SPLITS", 1); // int how many levels of subfolders to put in the warehouse
_d("VERSION", '2.7-beta'); // string shimmie version _d("VERSION", '2.7-beta'); // string shimmie version
_d("TIMEZONE", null); // string timezone _d("TIMEZONE", null); // string timezone
_d("CORE_EXTS", "bbcode,user,mail,upload,image,view,handle_pixel,ext_manager,setup,upgrade,handle_404,handle_static,comment,tag_list,index,tag_edit,alias_editor,graphics"); // extensions to always enable _d("CORE_EXTS", "bbcode,user,mail,upload,image,view,handle_pixel,ext_manager,setup,upgrade,handle_404,handle_static,comment,tag_list,index,tag_edit,alias_editor,media"); // extensions to always enable
_d("EXTRA_EXTS", ""); // string optional extra extensions _d("EXTRA_EXTS", ""); // string optional extra extensions
_d("BASE_URL", null); // string force a specific base URL (default is auto-detect) _d("BASE_URL", null); // string force a specific base URL (default is auto-detect)
_d("MIN_PHP_VERSION", '7.1');// string minimum supported PHP version _d("MIN_PHP_VERSION", '7.1');// string minimum supported PHP version

View File

@ -79,7 +79,7 @@ function get_memory_limit(): int
// thumbnail generation requires lots of memory // thumbnail generation requires lots of memory
$default_limit = 8*1024*1024; // 8 MB of memory is PHP's default. $default_limit = 8*1024*1024; // 8 MB of memory is PHP's default.
$shimmie_limit = parse_shorthand_int($config->get_int(GraphicsConfig::MEM_LIMIT)); $shimmie_limit = parse_shorthand_int($config->get_int(MediaConfig::MEM_LIMIT));
if ($shimmie_limit < 3*1024*1024) { if ($shimmie_limit < 3*1024*1024) {
// we aren't going to fit, override // we aren't going to fit, override

View File

@ -53,9 +53,9 @@ class ET extends Extension
to_shorthand_int(disk_total_space("./")); to_shorthand_int(disk_total_space("./"));
$info['sys_server'] = isset($_SERVER["SERVER_SOFTWARE"]) ? $_SERVER["SERVER_SOFTWARE"] : 'unknown'; $info['sys_server'] = isset($_SERVER["SERVER_SOFTWARE"]) ? $_SERVER["SERVER_SOFTWARE"] : 'unknown';
$info[GraphicsConfig::FFMPEG_PATH] = $config->get_string(GraphicsConfig::FFMPEG_PATH); $info[MediaConfig::FFMPEG_PATH] = $config->get_string(MediaConfig::FFMPEG_PATH);
$info[GraphicsConfig::CONVERT_PATH] = $config->get_string(GraphicsConfig::CONVERT_PATH); $info[MediaConfig::CONVERT_PATH] = $config->get_string(MediaConfig::CONVERT_PATH);
$info[GraphicsConfig::MEM_LIMIT] = $config->get_int(GraphicsConfig::MEM_LIMIT); $info[MediaConfig::MEM_LIMIT] = $config->get_int(MediaConfig::MEM_LIMIT);
$info[ImageConfig::THUMB_ENGINE] = $config->get_string(ImageConfig::THUMB_ENGINE); $info[ImageConfig::THUMB_ENGINE] = $config->get_string(ImageConfig::THUMB_ENGINE);
$info[ImageConfig::THUMB_QUALITY] = $config->get_int(ImageConfig::THUMB_QUALITY); $info[ImageConfig::THUMB_QUALITY] = $config->get_int(ImageConfig::THUMB_QUALITY);

View File

@ -35,8 +35,8 @@ Database: {$info['sys_db']}
Server: {$info['sys_server']} Server: {$info['sys_server']}
Disk use: {$info['sys_disk']} Disk use: {$info['sys_disk']}
Graphics System: Media System:
Memory Limit: {$info[GraphicsConfig::MEM_LIMIT]} Memory Limit: {$info[MediaConfig::MEM_LIMIT]}
Thumbnail Generation: Thumbnail Generation:
Engine: {$info[ImageConfig::THUMB_ENGINE]} Engine: {$info[ImageConfig::THUMB_ENGINE]}

View File

@ -1,6 +0,0 @@
<?php
class GraphicsTheme extends Themelet
{
}

View File

@ -12,7 +12,7 @@ class FlashFileHandler extends DataHandlerExtension
{ {
global $config; global $config;
if (!Graphics::create_thumbnail_ffmpeg($hash)) { if (!Media::create_thumbnail_ffmpeg($hash)) {
copy("ext/handle_flash/thumb.jpg", warehouse_path(Image::THUMBNAIL_DIR, $hash)); copy("ext/handle_flash/thumb.jpg", warehouse_path(Image::THUMBNAIL_DIR, $hash));
} }
return true; return true;

View File

@ -57,9 +57,9 @@ class IcoFileHandler extends DataHandlerExtension
protected function create_thumb(string $hash, string $type): bool protected function create_thumb(string $hash, string $type): bool
{ {
try { try {
create_image_thumb($hash, $type, GraphicsEngine::IMAGICK); create_image_thumb($hash, $type, MediaEngine::IMAGICK);
return true; return true;
} catch (GraphicsException $e) { } catch (MediaException $e) {
log_warning("handle_ico", "Could not generate thumbnail. " . $e->getMessage()); log_warning("handle_ico", "Could not generate thumbnail. " . $e->getMessage());
return false; return false;
} }

View File

@ -36,9 +36,9 @@ class SVGFileHandler extends DataHandlerExtension
protected function create_thumb(string $hash, string $type): bool protected function create_thumb(string $hash, string $type): bool
{ {
try { try {
create_image_thumb($hash, $type, GraphicsEngine::IMAGICK); create_image_thumb($hash, $type, MediaEngine::IMAGICK);
return true; return true;
} catch (GraphicsException $e) { } catch (MediaException $e) {
log_warning("handle_svg", "Could not generate thumbnail. " . $e->getMessage()); log_warning("handle_svg", "Could not generate thumbnail. " . $e->getMessage());
copy("ext/handle_svg/thumb.jpg", warehouse_path(Image::THUMBNAIL_DIR, $hash)); copy("ext/handle_svg/thumb.jpg", warehouse_path(Image::THUMBNAIL_DIR, $hash));
return false; return false;

View File

@ -53,7 +53,7 @@ class VideoFileHandler extends DataHandlerExtension
*/ */
protected function create_thumb(string $hash, string $type): bool protected function create_thumb(string $hash, string $type): bool
{ {
return Graphics::create_thumbnail_ffmpeg($hash); return Media::create_thumbnail_ffmpeg($hash);
} }
protected function supported_ext(string $ext): bool protected function supported_ext(string $ext): bool
@ -65,7 +65,7 @@ class VideoFileHandler extends DataHandlerExtension
{ {
$image = new Image(); $image = new Image();
$size = Graphics::video_size($filename); $size = Media::video_size($filename);
$image->width = $size[0]; $image->width = $size[0];
$image->height = $size[1]; $image->height = $size[1];

View File

@ -41,8 +41,8 @@ class ImageIO extends Extension
const THUMBNAIL_ENGINES = [ const THUMBNAIL_ENGINES = [
'Built-in GD' => GraphicsEngine::GD, 'Built-in GD' => MediaEngine::GD,
'ImageMagick' => GraphicsEngine::IMAGICK 'ImageMagick' => MediaEngine::IMAGICK
]; ];
const THUMBNAIL_TYPES = [ const THUMBNAIL_TYPES = [

View File

@ -1,65 +1,66 @@
<?php <?php
/* /*
* Name: Graphics * Name: Media
* Author: Matthew Barbour <matthew@darkholme.net> * Author: Matthew Barbour <matthew@darkholme.net>
* Description: Provides common functions and settings used for graphic operations. * Description: Provides common functions and settings used for media operations.
* License: MIT * License: MIT
* Version: 1.0 * Version: 1.0
*/ */
/* /*
* This is used by the graphics code when there is an error * This is used by the media code when there is an error
*/ */
abstract class GraphicsConfig abstract class MediaConfig
{ {
const FFMPEG_PATH = "graphics_ffmpeg_path"; const FFMPEG_PATH = "media_ffmpeg_path";
const FFPROBE_PATH = "graphics_ffprobe_path"; const FFPROBE_PATH = "media_ffprobe_path";
const CONVERT_PATH = "graphics_convert_path"; const CONVERT_PATH = "media_convert_path";
const VERSION = "ext_graphics_version"; const VERSION = "ext_media_version";
const MEM_LIMIT = 'graphics_mem_limit'; const MEM_LIMIT = 'media_mem_limit';
} }
abstract class GraphicsEngine { abstract class MediaEngine
{
public const GD = "gd"; public const GD = "gd";
public const IMAGICK = "convert"; public const IMAGICK = "convert";
public const FFMPEG = "ffmpeg"; public const FFMPEG = "ffmpeg";
public const ALL = [ public const ALL = [
GraphicsEngine::GD, MediaEngine::GD,
GraphicsEngine::FFMPEG, MediaEngine::FFMPEG,
GraphicsEngine::IMAGICK MediaEngine::IMAGICK
]; ];
public const OUTPUT_SUPPORT = [ public const OUTPUT_SUPPORT = [
GraphicsEngine::GD => [ MediaEngine::GD => [
"gif", "gif",
"jpg", "jpg",
"png", "png",
"webp", "webp",
Graphics::WEBP_LOSSY, Media::WEBP_LOSSY,
], ],
GraphicsEngine::IMAGICK => [ MediaEngine::IMAGICK => [
"gif", "gif",
"jpg", "jpg",
"png", "png",
"webp", "webp",
Graphics::WEBP_LOSSY, Media::WEBP_LOSSY,
Graphics::WEBP_LOSSLESS, Media::WEBP_LOSSLESS,
], ],
GraphicsEngine::FFMPEG => [ MediaEngine::FFMPEG => [
] ]
]; ];
public const INPUT_SUPPORT = [ public const INPUT_SUPPORT = [
GraphicsEngine::GD => [ MediaEngine::GD => [
"bmp", "bmp",
"gif", "gif",
"jpg", "jpg",
"png", "png",
"webp", "webp",
], ],
GraphicsEngine::IMAGICK => [ MediaEngine::IMAGICK => [
"bmp", "bmp",
"gif", "gif",
"jpg", "jpg",
@ -69,7 +70,7 @@ abstract class GraphicsEngine {
"webp", "webp",
"ico", "ico",
], ],
GraphicsEngine::FFMPEG => [ MediaEngine::FFMPEG => [
"avi", "avi",
"mkv", "mkv",
"webm", "webm",
@ -80,11 +81,11 @@ abstract class GraphicsEngine {
]; ];
} }
class GraphicsException extends SCoreException class MediaException extends SCoreException
{ {
} }
class GraphicResizeEvent extends Event class MediaResizeEvent extends Event
{ {
public $engine; public $engine;
public $input_path; public $input_path;
@ -106,7 +107,7 @@ class GraphicResizeEvent extends Event
bool $minimize = false, bool $minimize = false,
bool $allow_upscale = true) bool $allow_upscale = true)
{ {
assert(in_array($engine, GraphicsEngine::ALL)); assert(in_array($engine, MediaEngine::ALL));
$this->engine = $engine; $this->engine = $engine;
$this->input_path = $input_path; $this->input_path = $input_path;
$this->input_type = $input_type; $this->input_type = $input_type;
@ -121,14 +122,28 @@ class GraphicResizeEvent extends Event
} }
} }
class Graphics extends Extension class MediaCheckLosslessEvent extends Event
{
public $file_name;
public $ext;
public $result = false;
public function __construct(string $file_name, string $ext)
{
$this->file_name = $file_name;
$this->ext = $ext;
}
}
class Media extends Extension
{ {
const WEBP_LOSSY = "webp-lossy"; const WEBP_LOSSY = "webp-lossy";
const WEBP_LOSSLESS = "webp-lossless"; const WEBP_LOSSLESS = "webp-lossless";
const IMAGE_GRAPHICS_ENGINES = [ const IMAGE_MEDIA_ENGINES = [
"GD" => GraphicsEngine::GD, "GD" => MediaEngine::GD,
"ImageMagick" => GraphicsEngine::IMAGICK, "ImageMagick" => MediaEngine::IMAGICK,
]; ];
const LOSSLESS_FORMATS = [ const LOSSLESS_FORMATS = [
@ -148,6 +163,16 @@ class Graphics extends Extension
]; ];
//RIFF####WEBPVP8?..............ANIM
private const WEBP_ANIMATION_HEADER =
[0x52, 0x49, 0x46, 0x46, null, null, null, null, 0x57, 0x45, 0x42, 0x50, 0x56, 0x50, 0x38, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0x41, 0x4E, 0x49, 0x4D];
//RIFF####WEBPVP8L
private const WEBP_LOSSLESS_HEADER =
[0x52, 0x49, 0x46, 0x46, null, null, null, null, 0x57, 0x45, 0x42, 0x50, 0x56, 0x50, 0x38, 0x4C];
static function imagick_available(): bool static function imagick_available(): bool
{ {
return extension_loaded("imagick"); return extension_loaded("imagick");
@ -164,46 +189,46 @@ class Graphics extends Extension
public function onInitExt(InitExtEvent $event) public function onInitExt(InitExtEvent $event)
{ {
global $config; global $config;
$config->set_default_string(GraphicsConfig::FFPROBE_PATH, 'ffprobe'); $config->set_default_string(MediaConfig::FFPROBE_PATH, 'ffprobe');
$config->set_default_int(GraphicsConfig::MEM_LIMIT, parse_shorthand_int('8MB')); $config->set_default_int(MediaConfig::MEM_LIMIT, parse_shorthand_int('8MB'));
$config->set_default_string(GraphicsConfig::FFMPEG_PATH, ''); $config->set_default_string(MediaConfig::FFMPEG_PATH, '');
$config->set_default_string(GraphicsConfig::CONVERT_PATH, 'convert'); $config->set_default_string(MediaConfig::CONVERT_PATH, 'convert');
if ($config->get_int(GraphicsConfig::VERSION) < 1) { if ($config->get_int(MediaConfig::VERSION) < 1) {
$current_value = $config->get_string("thumb_ffmpeg_path"); $current_value = $config->get_string("thumb_ffmpeg_path");
if (!empty($current_value)) { if (!empty($current_value)) {
$config->set_string(GraphicsConfig::FFMPEG_PATH, $current_value); $config->set_string(MediaConfig::FFMPEG_PATH, $current_value);
} elseif ($ffmpeg = shell_exec((PHP_OS == 'WINNT' ? 'where' : 'which') . ' ffmpeg')) { } elseif ($ffmpeg = shell_exec((PHP_OS == 'WINNT' ? 'where' : 'which') . ' ffmpeg')) {
//ffmpeg exists in PATH, check if it's executable, and if so, default to it instead of static //ffmpeg exists in PATH, check if it's executable, and if so, default to it instead of static
if (is_executable(strtok($ffmpeg, PHP_EOL))) { if (is_executable(strtok($ffmpeg, PHP_EOL))) {
$config->set_default_string(GraphicsConfig::FFMPEG_PATH, 'ffmpeg'); $config->set_default_string(MediaConfig::FFMPEG_PATH, 'ffmpeg');
} }
} }
$current_value = $config->get_string("thumb_convert_path"); $current_value = $config->get_string("thumb_convert_path");
if (!empty($current_value)) { if (!empty($current_value)) {
$config->set_string(GraphicsConfig::CONVERT_PATH, $current_value); $config->set_string(MediaConfig::CONVERT_PATH, $current_value);
} elseif ($convert = shell_exec((PHP_OS == 'WINNT' ? 'where' : 'which') . ' convert')) { } elseif ($convert = shell_exec((PHP_OS == 'WINNT' ? 'where' : 'which') . ' convert')) {
//ffmpeg exists in PATH, check if it's executable, and if so, default to it instead of static //ffmpeg exists in PATH, check if it's executable, and if so, default to it instead of static
if (is_executable(strtok($convert, PHP_EOL))) { if (is_executable(strtok($convert, PHP_EOL))) {
$config->set_default_string(GraphicsConfig::CONVERT_PATH, 'convert'); $config->set_default_string(MediaConfig::CONVERT_PATH, 'convert');
} }
} }
$current_value = $config->get_int("thumb_mem_limit"); $current_value = $config->get_int("thumb_mem_limit");
if (!empty($current_value)) { if (!empty($current_value)) {
$config->set_int(GraphicsConfig::MEM_LIMIT, $current_value); $config->set_int(MediaConfig::MEM_LIMIT, $current_value);
} }
$config->set_int(GraphicsConfig::VERSION, 1); $config->set_int(MediaConfig::VERSION, 1);
log_info("graphics", "extension installed"); log_info("media", "extension installed");
} }
} }
public function onSetupBuilding(SetupBuildingEvent $event) public function onSetupBuilding(SetupBuildingEvent $event)
{ {
$sb = new SetupBlock("Graphic Engines"); $sb = new SetupBlock("Media Engines");
// if (self::imagick_available()) { // if (self::imagick_available()) {
// try { // try {
@ -214,29 +239,29 @@ class Graphics extends Extension
// $sb->add_label("<b style='color:red'>ImageMagick not detected</b>"); // $sb->add_label("<b style='color:red'>ImageMagick not detected</b>");
// } // }
// } else { // } else {
$sb->add_text_option(GraphicsConfig::CONVERT_PATH, "convert command: "); $sb->add_text_option(MediaConfig::CONVERT_PATH, "convert command: ");
// } // }
$sb->add_text_option(GraphicsConfig::FFMPEG_PATH, "<br/>ffmpeg command: "); $sb->add_text_option(MediaConfig::FFMPEG_PATH, "<br/>ffmpeg command: ");
$sb->add_shorthand_int_option(GraphicsConfig::MEM_LIMIT, "<br />Max memory use: "); $sb->add_shorthand_int_option(MediaConfig::MEM_LIMIT, "<br />Max memory use: ");
$event->panel->add_block($sb); $event->panel->add_block($sb);
} }
/** /**
* @param GraphicResizeEvent $event * @param MediaResizeEvent $event
* @throws GraphicsException * @throws MediaException
* @throws InsufficientMemoryException * @throws InsufficientMemoryException
*/ */
public function onGraphicResize(GraphicResizeEvent $event) public function onMediaResize(MediaResizeEvent $event)
{ {
switch ($event->engine) { switch ($event->engine) {
case GraphicsEngine::GD: case MediaEngine::GD:
$info = getimagesize($event->input_path); $info = getimagesize($event->input_path);
if ($info === false) { if ($info === false) {
throw new GraphicsException("getimagesize failed for " . $event->input_path); throw new MediaException("getimagesize failed for " . $event->input_path);
} }
self::image_resize_gd( self::image_resize_gd(
@ -251,7 +276,7 @@ class Graphics extends Extension
$event->allow_upscale); $event->allow_upscale);
break; break;
case GraphicsEngine::IMAGICK: case MediaEngine::IMAGICK:
// if (self::imagick_available()) { // if (self::imagick_available()) {
// } else { // } else {
self::image_resize_convert( self::image_resize_convert(
@ -268,7 +293,7 @@ class Graphics extends Extension
//} //}
break; break;
default: default:
throw new GraphicsException("Engine not supported for resize: " . $event->engine); throw new MediaException("Engine not supported for resize: " . $event->engine);
} }
// TODO: Get output optimization tools working better // TODO: Get output optimization tools working better
@ -277,6 +302,22 @@ class Graphics extends Extension
// } // }
} }
public function onMediaCheckLossless(MediaCheckLosslessEvent $event)
{
switch ($event->ext) {
case "png":
case "psd":
case "bmp":
case "gif":
case "ico":
$event->result = true;
break;
case "webp":
$event->result = Media::is_lossless_webp($event->file_name);
break;
}
}
/** /**
* Check Memory usage limits * Check Memory usage limits
* *
@ -310,15 +351,15 @@ class Graphics extends Extension
* *
* @param $hash * @param $hash
* @return bool true if successful, false if not. * @return bool true if successful, false if not.
* @throws GraphicsException * @throws MediaException
*/ */
public static function create_thumbnail_ffmpeg($hash): bool public static function create_thumbnail_ffmpeg($hash): bool
{ {
global $config; global $config;
$ffmpeg = $config->get_string(GraphicsConfig::FFMPEG_PATH); $ffmpeg = $config->get_string(MediaConfig::FFMPEG_PATH);
if ($ffmpeg == null || $ffmpeg == "") { if ($ffmpeg == null || $ffmpeg == "") {
throw new GraphicsException("ffmpeg command configured"); throw new MediaException("ffmpeg command configured");
} }
$inname = warehouse_path(Image::IMAGE_DIR, $hash); $inname = warehouse_path(Image::IMAGE_DIR, $hash);
@ -355,10 +396,10 @@ class Graphics extends Extension
exec($cmd, $output, $ret); exec($cmd, $output, $ret);
if ((int)$ret == (int)0) { if ((int)$ret == (int)0) {
log_debug('graphics', "Generating thumbnail with command `$cmd`, returns $ret"); log_debug('Media', "Generating thumbnail with command `$cmd`, returns $ret");
return true; return true;
} else { } else {
log_error('graphics', "Generating thumbnail with command `$cmd`, returns $ret"); log_error('Media', "Generating thumbnail with command `$cmd`, returns $ret");
return false; return false;
} }
} }
@ -498,10 +539,10 @@ class Graphics extends Extension
{ {
global $config; global $config;
$convert = $config->get_string(GraphicsConfig::CONVERT_PATH); $convert = $config->get_string(MediaConfig::CONVERT_PATH);
if (empty($convert)) { if (empty($convert)) {
throw new GraphicsException("convert command not configured"); throw new MediaException("convert command not configured");
} }
if (empty($output_type)) { if (empty($output_type)) {
@ -533,9 +574,9 @@ class Graphics extends Extension
$cmd = str_replace("\"convert\"", "convert", $cmd); // quotes are only needed if the path to convert contains a space; some other times, quotes break things, see github bug #27 $cmd = str_replace("\"convert\"", "convert", $cmd); // quotes are only needed if the path to convert contains a space; some other times, quotes break things, see github bug #27
exec($cmd, $output, $ret); exec($cmd, $output, $ret);
if ($ret != 0) { if ($ret != 0) {
throw new GraphicsException("Resizing image with command `$cmd`, returns $ret, outputting " . implode("\r\n", $output)); throw new MediaException("Resizing image with command `$cmd`, returns $ret, outputting " . implode("\r\n", $output));
} else { } else {
log_debug('graphics', "Generating thumbnail with command `$cmd`, returns $ret"); log_debug('Media', "Generating thumbnail with command `$cmd`, returns $ret");
} }
} }
@ -549,7 +590,7 @@ class Graphics extends Extension
* @param string $output_filename * @param string $output_filename
* @param string|null $output_type If set to null, the output file type will be automatically determined via the $info parameter. Otherwise an exception will be thrown. * @param string|null $output_type If set to null, the output file type will be automatically determined via the $info parameter. Otherwise an exception will be thrown.
* @param int $output_quality Defaults to 80. * @param int $output_quality Defaults to 80.
* @throws GraphicsException * @throws MediaException
* @throws InsufficientMemoryException if the estimated memory usage exceeds the memory limit. * @throws InsufficientMemoryException if the estimated memory usage exceeds the memory limit.
*/ */
public static function image_resize_gd( public static function image_resize_gd(
@ -586,7 +627,7 @@ class Graphics extends Extension
$output_type = "bmp"; $output_type = "bmp";
break; break;
default: default:
throw new GraphicsException("Failed to save the new image - Unsupported image type."); throw new MediaException("Failed to save the new image - Unsupported image type.");
} }
} }
@ -609,10 +650,10 @@ class Graphics extends Extension
$image_resized = imagecreatetruecolor($new_width, $new_height); $image_resized = imagecreatetruecolor($new_width, $new_height);
try { try {
if ($image === false) { if ($image === false) {
throw new GraphicsException("Could not load image: " . $image_filename); throw new MediaException("Could not load image: " . $image_filename);
} }
if ($image_resized === false) { if ($image_resized === false) {
throw new GraphicsException("Could not create output image with dimensions $new_width c $new_height "); throw new MediaException("Could not create output image with dimensions $new_width c $new_height ");
} }
// Handle transparent images // Handle transparent images
@ -629,12 +670,12 @@ class Graphics extends Extension
// Allocate the same color in the new image resource // Allocate the same color in the new image resource
$transparency = imagecolorallocate($image_resized, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']); $transparency = imagecolorallocate($image_resized, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']);
if ($transparency === false) { if ($transparency === false) {
throw new GraphicsException("Unable to allocate transparent color"); throw new MediaException("Unable to allocate transparent color");
} }
// Completely fill the background of the new image with allocated color. // Completely fill the background of the new image with allocated color.
if (imagefill($image_resized, 0, 0, $transparency) === false) { if (imagefill($image_resized, 0, 0, $transparency) === false) {
throw new GraphicsException("Unable to fill new image with transparent color"); throw new MediaException("Unable to fill new image with transparent color");
} }
// Set the background color for new image to transparent // Set the background color for new image to transparent
@ -647,17 +688,17 @@ class Graphics extends Extension
// More info here: http://stackoverflow.com/questions/279236/how-do-i-resize-pngs-with-transparency-in-php // More info here: http://stackoverflow.com/questions/279236/how-do-i-resize-pngs-with-transparency-in-php
// //
if (imagealphablending($image_resized, false) === false) { if (imagealphablending($image_resized, false) === false) {
throw new GraphicsException("Unable to disable image alpha blending"); throw new MediaException("Unable to disable image alpha blending");
} }
if (imagesavealpha($image_resized, true) === false) { if (imagesavealpha($image_resized, true) === false) {
throw new GraphicsException("Unable to enable image save alpha"); throw new MediaException("Unable to enable image save alpha");
} }
$transparent_color = imagecolorallocatealpha($image_resized, 255, 255, 255, 127); $transparent_color = imagecolorallocatealpha($image_resized, 255, 255, 255, 127);
if ($transparent_color === false) { if ($transparent_color === false) {
throw new GraphicsException("Unable to allocate transparent color"); throw new MediaException("Unable to allocate transparent color");
} }
if (imagefilledrectangle($image_resized, 0, 0, $new_width, $new_height, $transparent_color) === false) { if (imagefilledrectangle($image_resized, 0, 0, $new_width, $new_height, $transparent_color) === false) {
throw new GraphicsException("Unable to fill new image with transparent color"); throw new MediaException("Unable to fill new image with transparent color");
} }
break; break;
} }
@ -675,7 +716,7 @@ class Graphics extends Extension
$width, $width,
$height $height
) === false) { ) === false) {
throw new GraphicsException("Unable to copy resized image data to new image"); throw new MediaException("Unable to copy resized image data to new image");
} }
switch ($output_type) { switch ($output_type) {
@ -683,7 +724,7 @@ class Graphics extends Extension
$result = imagebmp($image_resized, $output_filename, true); $result = imagebmp($image_resized, $output_filename, true);
break; break;
case "webp": case "webp":
case Graphics::WEBP_LOSSY: case Media::WEBP_LOSSY:
$result = imagewebp($image_resized, $output_filename, $output_quality); $result = imagewebp($image_resized, $output_filename, $output_quality);
break; break;
case "jpg": case "jpg":
@ -697,10 +738,10 @@ class Graphics extends Extension
$result = imagegif($image_resized, $output_filename); $result = imagegif($image_resized, $output_filename);
break; break;
default: default:
throw new GraphicsException("Failed to save the new image - Unsupported image type: $output_type"); throw new MediaException("Failed to save the new image - Unsupported image type: $output_type");
} }
if ($result === false) { if ($result === false) {
throw new GraphicsException("Failed to save the new image, function returned false when saving type: $output_type"); throw new MediaException("Failed to save the new image, function returned false when saving type: $output_type");
} }
} finally { } finally {
@imagedestroy($image); @imagedestroy($image);
@ -714,19 +755,69 @@ class Graphics extends Extension
* @param String $image_filename The path of the file to check. * @param String $image_filename The path of the file to check.
* @return bool true if the file is an animated gif, false if it is not. * @return bool true if the file is an animated gif, false if it is not.
*/ */
public static function is_animated_gif(String $image_filename) public static function is_animated_gif(String $image_filename): bool
{ {
$is_anim_gif = 0; $is_anim_gif = 0;
if (($fh = @fopen($image_filename, 'rb'))) { if (($fh = @fopen($image_filename, 'rb'))) {
//check if gif is animated (via http://www.php.net/manual/en/function.imagecreatefromgif.php#104473) try {
while (!feof($fh) && $is_anim_gif < 2) { //check if gif is animated (via http://www.php.net/manual/en/function.imagecreatefromgif.php#104473)
$chunk = fread($fh, 1024 * 100); while (!feof($fh) && $is_anim_gif < 2) {
$is_anim_gif += preg_match_all('#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches); $chunk = fread($fh, 1024 * 100);
$is_anim_gif += preg_match_all('#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches);
}
} finally {
@fclose($fh);
} }
} }
return ($is_anim_gif == 0); return ($is_anim_gif == 0);
} }
private static function compare_file_bytes(String $file_name, array $comparison): bool
{
$size= filesize($file_name);
if ($size < count($comparison)) {
// Can't match because it's too small
return false;
}
if (($fh = @fopen($file_name, 'rb'))) {
try {
$chunk = unpack("C*",fread($fh, count($comparison)));
for ($i = 0; $i < count($comparison); $i++) {
$byte = $comparison[$i];
if($byte==null) {
continue;
} else {
$fileByte = $chunk[$i+1];
if($fileByte!=$byte) {
return false;
}
}
}
return true;
} finally {
@fclose($fh);
}
} else {
throw new MediaException("Unable to open file for byte check: $file_name");
}
}
public static function is_animated_webp(String $image_filename): bool
{
return self::compare_file_bytes($image_filename, self::WEBP_ANIMATION_HEADER);
}
public static function is_lossless_webp(String $image_filename): bool
{
return self::compare_file_bytes($image_filename, self::WEBP_LOSSLESS_HEADER);
}
public static function supports_alpha(string $format) public static function supports_alpha(string $format)
{ {
return in_array(self::normalize_format($format), self::ALPHA_FORMATS); return in_array(self::normalize_format($format), self::ALPHA_FORMATS);
@ -735,7 +826,7 @@ class Graphics extends Extension
public static function is_input_supported($engine, $format): bool public static function is_input_supported($engine, $format): bool
{ {
$format = self::normalize_format($format); $format = self::normalize_format($format);
if (!in_array($format, GraphicsEngine::INPUT_SUPPORT[$engine])) { if (!in_array($format, MediaEngine::INPUT_SUPPORT[$engine])) {
return false; return false;
} }
return true; return true;
@ -744,7 +835,7 @@ class Graphics extends Extension
public static function is_output_supported($engine, $format): bool public static function is_output_supported($engine, $format): bool
{ {
$format = self::normalize_format($format); $format = self::normalize_format($format);
if (!in_array($format, GraphicsEngine::OUTPUT_SUPPORT[$engine])) { if (!in_array($format, MediaEngine::OUTPUT_SUPPORT[$engine])) {
return false; return false;
} }
return true; return true;
@ -752,15 +843,15 @@ class Graphics extends Extension
/** /**
* Checks if a format (normally a file extension) is a variant name of another format (ie, jpg and jpeg). * Checks if a format (normally a file extension) is a variant name of another format (ie, jpg and jpeg).
* If one is found, then the maine name that the Graphics extension will recognize is returned, * If one is found, then the maine name that the Media extension will recognize is returned,
* otherwise the incoming format is returned. * otherwise the incoming format is returned.
* *
* @param $format * @param $format
* @return string|null The format name that the graphics extension will recognize. * @return string|null The format name that the media extension will recognize.
*/ */
static public function normalize_format($format): ?string static public function normalize_format($format): ?string
{ {
if (array_key_exists($format, Graphics::FORMAT_ALIASES)) { if (array_key_exists($format, Media::FORMAT_ALIASES)) {
return self::FORMAT_ALIASES[$format]; return self::FORMAT_ALIASES[$format];
} }
return $format; return $format;
@ -776,7 +867,7 @@ class Graphics extends Extension
static public function video_size(string $filename): array static public function video_size(string $filename): array
{ {
global $config; global $config;
$ffmpeg = $config->get_string(GraphicsConfig::FFMPEG_PATH); $ffmpeg = $config->get_string(MediaConfig::FFMPEG_PATH);
$cmd = escapeshellcmd(implode(" ", [ $cmd = escapeshellcmd(implode(" ", [
escapeshellarg($ffmpeg), escapeshellarg($ffmpeg),
"-y", "-i", escapeshellarg($filename), "-y", "-i", escapeshellarg($filename),
@ -795,7 +886,7 @@ class Graphics extends Extension
} else { } else {
$size = [1, 1]; $size = [1, 1];
} }
log_debug('graphics', "Getting video size with `$cmd`, returns $output -- $size[0], $size[1]"); log_debug('Media', "Getting video size with `$cmd`, returns $output -- $size[0], $size[1]");
return $size; return $size;
} }

6
ext/media/theme.php Normal file
View File

@ -0,0 +1,6 @@
<?php
class MediaTheme extends Themelet
{
}

View File

@ -40,7 +40,7 @@ class ResizeImage extends Extension
global $config; global $config;
$config->set_default_bool(ResizeConfig::ENABLED, true); $config->set_default_bool(ResizeConfig::ENABLED, true);
$config->set_default_bool(ResizeConfig::UPLOAD, false); $config->set_default_bool(ResizeConfig::UPLOAD, false);
$config->set_default_string(ResizeConfig::ENGINE, GraphicsEngine::GD); $config->set_default_string(ResizeConfig::ENGINE, MediaEngine::GD);
$config->set_default_int(ResizeConfig::DEFAULT_WIDTH, 0); $config->set_default_int(ResizeConfig::DEFAULT_WIDTH, 0);
$config->set_default_int(ResizeConfig::DEFAULT_HEIGHT, 0); $config->set_default_int(ResizeConfig::DEFAULT_HEIGHT, 0);
} }
@ -174,8 +174,8 @@ class ResizeImage extends Extension
{ {
global $config; global $config;
$engine = $config->get_string(ResizeConfig::ENGINE); $engine = $config->get_string(ResizeConfig::ENGINE);
return Graphics::is_input_supported($engine, $format) return Media::is_input_supported($engine, $format)
&& Graphics::is_output_supported($engine, $format); && Media::is_output_supported($engine, $format);
} }
@ -205,8 +205,8 @@ class ResizeImage extends Extension
throw new ImageResizeException("Unable to save temporary image file."); throw new ImageResizeException("Unable to save temporary image file.");
} }
send_event(new GraphicResizeEvent( send_event(new MediaResizeEvent(
GraphicsEngine::GD, MediaEngine::GD,
$image_filename, $image_filename,
$image_obj->ext, $image_obj->ext,
$tmp_filename, $tmp_filename,

View File

@ -130,7 +130,7 @@ class RotateImage extends Extension
$info = getimagesize($image_filename); $info = getimagesize($image_filename);
$memory_use = Graphics::calc_memory_use($info); $memory_use = Media::calc_memory_use($info);
$memory_limit = get_memory_limit(); $memory_limit = get_memory_limit();
if ($memory_use > $memory_limit) { if ($memory_use > $memory_limit) {

View File

@ -48,8 +48,8 @@ class TranscodeImage extends Extension
"" => "", "" => "",
"JPEG (lossy)" => "jpg", "JPEG (lossy)" => "jpg",
"PNG (lossless)" => "png", "PNG (lossless)" => "png",
"WEBP (lossy)" => Graphics::WEBP_LOSSY, "WEBP (lossy)" => Media::WEBP_LOSSY,
"WEBP (lossless)" => Graphics::WEBP_LOSSLESS, "WEBP (lossless)" => Media::WEBP_LOSSLESS,
]; ];
/** /**
@ -66,7 +66,7 @@ class TranscodeImage extends Extension
global $config; global $config;
$config->set_default_bool(TranscodeConfig::ENABLED, true); $config->set_default_bool(TranscodeConfig::ENABLED, true);
$config->set_default_bool(TranscodeConfig::UPLOAD, false); $config->set_default_bool(TranscodeConfig::UPLOAD, false);
$config->set_default_string(TranscodeConfig::ENGINE, GraphicsEngine::GD); $config->set_default_string(TranscodeConfig::ENGINE, MediaEngine::GD);
$config->set_default_int(TranscodeConfig::QUALITY, 80); $config->set_default_int(TranscodeConfig::QUALITY, 80);
foreach (array_values(self::INPUT_FORMATS) as $format) { foreach (array_values(self::INPUT_FORMATS) as $format) {
@ -98,9 +98,9 @@ class TranscodeImage extends Extension
$sb->start_table(); $sb->start_table();
$sb->add_bool_option(TranscodeConfig::ENABLED, "Allow transcoding images: ", true); $sb->add_bool_option(TranscodeConfig::ENABLED, "Allow transcoding images: ", true);
$sb->add_bool_option(TranscodeConfig::UPLOAD, "Transcode on upload: ", true); $sb->add_bool_option(TranscodeConfig::UPLOAD, "Transcode on upload: ", true);
$sb->add_choice_option(TranscodeConfig::ENGINE, Graphics::IMAGE_GRAPHICS_ENGINES, "Engine", true); $sb->add_choice_option(TranscodeConfig::ENGINE, Media::IMAGE__MEDIA_ENGINES, "Engine", true);
foreach (self::INPUT_FORMATS as $display=>$format) { foreach (self::INPUT_FORMATS as $display=>$format) {
if (in_array($format, GraphicsEngine::INPUT_SUPPORT[$engine])) { if (in_array($format, MediaEngine::INPUT_SUPPORT[$engine])) {
$outputs = $this->get_supported_output_formats($engine, $format); $outputs = $this->get_supported_output_formats($engine, $format);
$sb->add_choice_option(TranscodeConfig::UPLOAD_PREFIX.$format, $outputs, "$display", true); $sb->add_choice_option(TranscodeConfig::UPLOAD_PREFIX.$format, $outputs, "$display", true);
} }
@ -117,9 +117,9 @@ class TranscodeImage extends Extension
if ($config->get_bool(TranscodeConfig::UPLOAD) == true) { if ($config->get_bool(TranscodeConfig::UPLOAD) == true) {
$ext = strtolower($event->type); $ext = strtolower($event->type);
$ext = Graphics::normalize_format($ext); $ext = Media::normalize_format($ext);
if ($event->type=="gif"&&Graphics::is_animated_gif($event->tmpname)) { if ($event->type=="gif"&&Media::is_animated_gif($event->tmpname)) {
return; return;
} }
@ -130,7 +130,7 @@ class TranscodeImage extends Extension
} }
try { try {
$new_image = $this->transcode_image($event->tmpname, $ext, $target_format); $new_image = $this->transcode_image($event->tmpname, $ext, $target_format);
$event->set_type(Graphics::determine_ext($target_format)); $event->set_type(Media::determine_ext($target_format));
$event->set_tmpname($new_image); $event->set_tmpname($new_image);
} catch (Exception $e) { } catch (Exception $e) {
log_error("transcode", "Error while performing upload transcode: ".$e->getMessage()); log_error("transcode", "Error while performing upload transcode: ".$e->getMessage());
@ -224,21 +224,21 @@ class TranscodeImage extends Extension
private function can_convert_format($engine, $format): bool private function can_convert_format($engine, $format): bool
{ {
return Graphics::is_input_supported($engine, $format); return Media::is_input_supported($engine, $format);
} }
private function get_supported_output_formats($engine, ?String $omit_format = null): array private function get_supported_output_formats($engine, ?String $omit_format = null): array
{ {
$omit_format = Graphics::normalize_format($omit_format); $omit_format = Media::normalize_format($omit_format);
$output = []; $output = [];
foreach (self::OUTPUT_FORMATS as $key=>$value) { foreach (self::OUTPUT_FORMATS as $key=>$value) {
if ($value=="") { if ($value=="") {
$output[$key] = $value; $output[$key] = $value;
continue; continue;
} }
if(Graphics::is_output_supported($engine, $value) if(Media::is_output_supported($engine, $value)
&&(empty($omit_format)||$omit_format!=Graphics::determine_ext($value))) { &&(empty($omit_format)||$omit_format!=Media::determine_ext($value))) {
$output[$key] = $value; $output[$key] = $value;
} }
} }
@ -259,7 +259,7 @@ class TranscodeImage extends Extension
$new_image->filename = $image_obj->filename; $new_image->filename = $image_obj->filename;
$new_image->width = $image_obj->width; $new_image->width = $image_obj->width;
$new_image->height = $image_obj->height; $new_image->height = $image_obj->height;
$new_image->ext = Graphics::determine_ext($target_format); $new_image->ext = Media::determine_ext($target_format);
/* Move the new image into the main storage location */ /* Move the new image into the main storage location */
$target = warehouse_path(Image::IMAGE_DIR, $new_image->hash); $target = warehouse_path(Image::IMAGE_DIR, $new_image->hash);
@ -278,7 +278,7 @@ class TranscodeImage extends Extension
{ {
global $config; global $config;
if ($source_format==Graphics::determine_ext($target_format)) { if ($source_format==Media::determine_ext($target_format)) {
throw new ImageTranscodeException("Source and target formats are the same: ".$source_format); throw new ImageTranscodeException("Source and target formats are the same: ".$source_format);
} }
@ -289,7 +289,7 @@ class TranscodeImage extends Extension
if (!$this->can_convert_format($engine, $source_format)) { if (!$this->can_convert_format($engine, $source_format)) {
throw new ImageTranscodeException("Engine $engine does not support input format $source_format"); throw new ImageTranscodeException("Engine $engine does not support input format $source_format");
} }
if (!in_array($target_format, GraphicsEngine::OUTPUT_SUPPORT[$engine])) { if (!in_array($target_format, MediaEngine::OUTPUT_SUPPORT[$engine])) {
throw new ImageTranscodeException("Engine $engine does not support output format $target_format"); throw new ImageTranscodeException("Engine $engine does not support output format $target_format");
} }
@ -313,7 +313,7 @@ class TranscodeImage extends Extension
try { try {
$result = false; $result = false;
switch ($target_format) { switch ($target_format) {
case "webp-lossy": case Media::WEBP_LOSSY:
$result = imagewebp($image, $tmp_name, $q); $result = imagewebp($image, $tmp_name, $q);
break; break;
case "png": case "png":
@ -358,20 +358,20 @@ class TranscodeImage extends Extension
global $config; global $config;
$q = $config->get_int("transcode_quality"); $q = $config->get_int("transcode_quality");
$convert = $config->get_string(GraphicsConfig::CONVERT_PATH); $convert = $config->get_string(MediaConfig::CONVERT_PATH);
if ($convert==null||$convert=="") { if ($convert==null||$convert=="") {
throw new ImageTranscodeException("ImageMagick path not configured"); throw new ImageTranscodeException("ImageMagick path not configured");
} }
$ext = Graphics::determine_ext($target_format); $ext = Media::determine_ext($target_format);
$args = " -flatten "; $args = " -flatten ";
$bg = "none"; $bg = "none";
switch ($target_format) { switch ($target_format) {
case Graphics::WEBP_LOSSLESS: case Media::WEBP_LOSSLESS:
$args .= '-define webp:lossless=true'; $args .= '-define webp:lossless=true';
break; break;
case Graphics::WEBP_LOSSY: case Media::WEBP_LOSSY:
$args .= ''; $args .= '';
break; break;
case "png": case "png":

View File

@ -18,6 +18,7 @@ class CustomViewImageTheme extends ViewImageTheme
$h_owner = html_escape($image->get_owner()->name); $h_owner = html_escape($image->get_owner()->name);
$h_ownerlink = "<a href='".make_link("user/$h_owner")."'>$h_owner</a>"; $h_ownerlink = "<a href='".make_link("user/$h_owner")."'>$h_owner</a>";
$h_ip = html_escape($image->owner_ip); $h_ip = html_escape($image->owner_ip);
$h_type = html_escape($image->get_mime_type());
$h_date = autodate($image->posted); $h_date = autodate($image->posted);
$h_filesize = to_shorthand_int($image->filesize); $h_filesize = to_shorthand_int($image->filesize);
@ -31,6 +32,7 @@ class CustomViewImageTheme extends ViewImageTheme
<br>Posted: $h_date by $h_ownerlink <br>Posted: $h_date by $h_ownerlink
<br>Size: {$image->width}x{$image->height} <br>Size: {$image->width}x{$image->height}
<br>Filesize: $h_filesize <br>Filesize: $h_filesize
<br>Type: ".$h_type."
"; ";
if (!is_null($image->source)) { if (!is_null($image->source)) {