503 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			503 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php declare(strict_types=1);
 | 
						|
 | 
						|
require_once "config.php";
 | 
						|
 /*
 | 
						|
 * This is used by the image transcoding code when there is an error while transcoding
 | 
						|
 */
 | 
						|
class ImageTranscodeException extends SCoreException
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
class TranscodeImage extends Extension
 | 
						|
{
 | 
						|
    /** @var TranscodeImageTheme */
 | 
						|
    protected ?Themelet $theme;
 | 
						|
 | 
						|
    const ACTION_BULK_TRANSCODE = "bulk_transcode";
 | 
						|
 | 
						|
    const INPUT_MIMES = [
 | 
						|
        "BMP" => MimeType::BMP,
 | 
						|
        "GIF" => MimeType::GIF,
 | 
						|
        "ICO" => MimeType::ICO,
 | 
						|
        "JPG" => MimeType::JPEG,
 | 
						|
        "PNG" => MimeType::PNG,
 | 
						|
        "PPM" => MimeType::PPM,
 | 
						|
        "PSD" => MimeType::PSD,
 | 
						|
        "TIFF" => MimeType::TIFF,
 | 
						|
        "WEBP" => MimeType::WEBP,
 | 
						|
        "TGA" => MimeType::TGA
 | 
						|
    ];
 | 
						|
 | 
						|
    const OUTPUT_MIMES = [
 | 
						|
        "" => "",
 | 
						|
        "JPEG (lossy)" => MimeType::JPEG,
 | 
						|
        "PNG (lossless)" => MimeType::PNG,
 | 
						|
        "WEBP (lossy)" => MimeType::WEBP,
 | 
						|
        "WEBP (lossless)" => MimeType::WEBP_LOSSLESS,
 | 
						|
    ];
 | 
						|
 | 
						|
    /**
 | 
						|
     * Needs to be after upload, but before the processing extensions
 | 
						|
     */
 | 
						|
    public function get_priority(): int
 | 
						|
    {
 | 
						|
        return 45;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    public function onInitExt(InitExtEvent $event)
 | 
						|
    {
 | 
						|
        global $config;
 | 
						|
        $config->set_default_bool(TranscodeConfig::ENABLED, true);
 | 
						|
        $config->set_default_bool(TranscodeConfig::GET_ENABLED, false);
 | 
						|
        $config->set_default_bool(TranscodeConfig::UPLOAD, false);
 | 
						|
        $config->set_default_string(TranscodeConfig::ENGINE, MediaEngine::GD);
 | 
						|
        $config->set_default_int(TranscodeConfig::QUALITY, 80);
 | 
						|
        $config->set_default_string(TranscodeConfig::ALPHA_COLOR, Media::DEFAULT_ALPHA_CONVERSION_COLOR);
 | 
						|
 | 
						|
        foreach (array_values(self::INPUT_MIMES) as $mime) {
 | 
						|
            $config->set_default_string(self::get_mapping_name($mime), "");
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    private static function get_mapping_name(string $mime): string
 | 
						|
    {
 | 
						|
        $mime = str_replace(".", "_", $mime);
 | 
						|
        $mime = str_replace("/", "_", $mime);
 | 
						|
        return TranscodeConfig::UPLOAD_PREFIX.$mime;
 | 
						|
    }
 | 
						|
 | 
						|
    private static function get_mapping(String $mime): ?string
 | 
						|
    {
 | 
						|
        global $config;
 | 
						|
        return $config->get_string(self::get_mapping_name($mime));
 | 
						|
    }
 | 
						|
    private static function set_mapping(String $from_mime, ?String $to_mime): void
 | 
						|
    {
 | 
						|
        global $config;
 | 
						|
        $config->set_string(self::get_mapping_name($from_mime), $to_mime);
 | 
						|
    }
 | 
						|
 | 
						|
    public static function get_enabled_mimes(): array
 | 
						|
    {
 | 
						|
        $output = [];
 | 
						|
        foreach (array_values(self::INPUT_MIMES) as $mime) {
 | 
						|
            $value = self::get_mapping($mime);
 | 
						|
            if (!empty($value)) {
 | 
						|
                $output[] = $mime;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return $output;
 | 
						|
    }
 | 
						|
 | 
						|
    public function onDatabaseUpgrade(DatabaseUpgradeEvent $event)
 | 
						|
    {
 | 
						|
        if ($this->get_version(TranscodeConfig::VERSION) < 1) {
 | 
						|
            $old_extensions =[];
 | 
						|
            foreach (array_values(self::INPUT_MIMES) as $mime) {
 | 
						|
                $old_extensions = array_merge($old_extensions, FileExtension::get_all_for_mime($mime));
 | 
						|
            }
 | 
						|
 | 
						|
            foreach ($old_extensions as $old_extension) {
 | 
						|
                $oldValue = $this->get_mapping($old_extension);
 | 
						|
                if (!empty($oldValue)) {
 | 
						|
                    $from_mime = MimeType::get_for_extension($old_extension);
 | 
						|
                    if (empty($from_mime)) {
 | 
						|
                        continue;
 | 
						|
                    }
 | 
						|
 | 
						|
                    $to_mime = MimeType::get_for_extension($oldValue);
 | 
						|
                    if (empty($to_mime)) {
 | 
						|
                        continue;
 | 
						|
                    }
 | 
						|
 | 
						|
                    $this->set_mapping($from_mime, $to_mime);
 | 
						|
                    $this->set_mapping($old_extension, null);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            $this->set_version(TranscodeConfig::VERSION, 1);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event)
 | 
						|
    {
 | 
						|
        global $user, $config;
 | 
						|
 | 
						|
        if ($user->can(Permissions::EDIT_FILES) && $event->context != "report") {
 | 
						|
            $engine = $config->get_string(TranscodeConfig::ENGINE);
 | 
						|
            if ($this->can_convert_mime($engine, $event->image->get_mime())) {
 | 
						|
                $options = $this->get_supported_output_mimes($engine, $event->image->get_mime());
 | 
						|
                $event->add_part($this->theme->get_transcode_html($event->image, $options));
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    public function onSetupBuilding(SetupBuildingEvent $event)
 | 
						|
    {
 | 
						|
        global $config;
 | 
						|
 | 
						|
        $engine = $config->get_string(TranscodeConfig::ENGINE);
 | 
						|
 | 
						|
 | 
						|
        $sb = $event->panel->create_new_block("Image Transcode");
 | 
						|
        $sb->start_table();
 | 
						|
        $sb->add_bool_option(TranscodeConfig::ENABLED, "Allow transcoding images", true);
 | 
						|
        $sb->add_bool_option(TranscodeConfig::GET_ENABLED, "Enable GET args", true);
 | 
						|
        $sb->add_bool_option(TranscodeConfig::UPLOAD, "Transcode on upload", true);
 | 
						|
        $sb->add_choice_option(TranscodeConfig::ENGINE, MediaEngine::IMAGE_ENGINES, "Engine", true);
 | 
						|
        foreach (self::INPUT_MIMES as $display=> $mime) {
 | 
						|
            if (MediaEngine::is_input_supported($engine, $mime)) {
 | 
						|
                $outputs = $this->get_supported_output_mimes($engine, $mime);
 | 
						|
                $sb->add_choice_option(self::get_mapping_name($mime), $outputs, "$display", true);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        $sb->add_int_option(TranscodeConfig::QUALITY, "Lossy Format Quality", true);
 | 
						|
        $sb->add_color_option(TranscodeConfig::ALPHA_COLOR, "Alpha Conversion Color", true);
 | 
						|
        $sb->end_table();
 | 
						|
    }
 | 
						|
 | 
						|
    public function onDataUpload(DataUploadEvent $event)
 | 
						|
    {
 | 
						|
        global $config;
 | 
						|
 | 
						|
        if ($config->get_bool(TranscodeConfig::UPLOAD) == true) {
 | 
						|
            $mime = strtolower($event->mime);
 | 
						|
            if ($mime===MimeType::GIF&&MimeType::is_animated_gif($event->tmpname)) {
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            if (in_array($mime, array_values(self::INPUT_MIMES))) {
 | 
						|
                $target_mime = self::get_mapping($mime);
 | 
						|
                if (empty($target_mime)) {
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
                try {
 | 
						|
                    $new_image = $this->transcode_image($event->tmpname, $mime, $target_mime);
 | 
						|
                    $event->set_mime($target_mime);
 | 
						|
                    $event->set_tmpname($new_image);
 | 
						|
                } catch (Exception $e) {
 | 
						|
                    log_error("transcode", "Error while performing upload transcode: ".$e->getMessage());
 | 
						|
                    // We don't want to interfere with the upload process,
 | 
						|
                    // so if something goes wrong the untranscoded image jsut continues
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
 | 
						|
    public function onPageRequest(PageRequestEvent $event)
 | 
						|
    {
 | 
						|
        global $page, $user;
 | 
						|
 | 
						|
        if ($event->page_matches("transcode") && $user->can(Permissions::EDIT_FILES)) {
 | 
						|
            if ($event->count_args() >= 1) {
 | 
						|
                $image_id = int_escape($event->get_arg(0));
 | 
						|
            } elseif (isset($_POST['image_id'])) {
 | 
						|
                $image_id =  int_escape($_POST['image_id']);
 | 
						|
            } else {
 | 
						|
                throw new ImageTranscodeException("Can not resize Image: No valid Post ID given.");
 | 
						|
            }
 | 
						|
            $image_obj = Image::by_id($image_id);
 | 
						|
            if (is_null($image_obj)) {
 | 
						|
                $this->theme->display_error(404, "Post not found", "No image in the database has the ID #$image_id");
 | 
						|
            } else {
 | 
						|
                if (isset($_POST['transcode_mime'])) {
 | 
						|
                    try {
 | 
						|
                        $this->transcode_and_replace_image($image_obj, $_POST['transcode_mime']);
 | 
						|
                        $page->set_mode(PageMode::REDIRECT);
 | 
						|
                        $page->set_redirect(make_link("post/view/".$image_id));
 | 
						|
                    } catch (ImageTranscodeException $e) {
 | 
						|
                        $this->theme->display_transcode_error($page, "Error Transcoding", $e->getMessage());
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    public function onImageDownloading(ImageDownloadingEvent $event)
 | 
						|
    {
 | 
						|
        global $config, $user;
 | 
						|
 | 
						|
        if ($config->get_bool(TranscodeConfig::GET_ENABLED) &&
 | 
						|
            isset($_GET['transcode']) &&
 | 
						|
            $user->can(Permissions::EDIT_FILES) &&
 | 
						|
            $this->can_convert_mime($config->get_string(TranscodeConfig::ENGINE), $event->image->get_mime())) {
 | 
						|
            $target_mime = $_GET['transcode'];
 | 
						|
 | 
						|
            if (!MimeType::is_mime($target_mime)) {
 | 
						|
                $target_mime = MimeType::get_for_extension($target_mime);
 | 
						|
            }
 | 
						|
            if (empty($target_mime)) {
 | 
						|
                throw new ImageTranscodeException("Unable to determine output MIME for ".$_GET['transcode']);
 | 
						|
            }
 | 
						|
 | 
						|
            MediaEngine::is_output_supported($config->get_string(TranscodeConfig::ENGINE), $target_mime);
 | 
						|
 | 
						|
            $source_mime = $event->image->get_mime();
 | 
						|
 | 
						|
            if ($source_mime!=$target_mime) {
 | 
						|
                $tmp_filename = $this->transcode_image($event->path, $source_mime, $target_mime);
 | 
						|
 | 
						|
                if ($event->file_modified===true&&$event->path!=$event->image->get_image_filename()) {
 | 
						|
                    // This means that we're dealing with a temp file that will need cleaned up
 | 
						|
                    unlink($event->path);
 | 
						|
                }
 | 
						|
 | 
						|
                $event->path = $tmp_filename;
 | 
						|
                $event->mime = $target_mime;
 | 
						|
                $event->file_modified = true;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    public function onBulkActionBlockBuilding(BulkActionBlockBuildingEvent $event)
 | 
						|
    {
 | 
						|
        global $user, $config;
 | 
						|
 | 
						|
        $engine = $config->get_string(TranscodeConfig::ENGINE);
 | 
						|
 | 
						|
        if ($user->can(Permissions::EDIT_FILES)) {
 | 
						|
            $event->add_action(self::ACTION_BULK_TRANSCODE, "Transcode Image", null, "", $this->theme->get_transcode_picker_html($this->get_supported_output_mimes($engine)));
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    public function onBulkAction(BulkActionEvent $event)
 | 
						|
    {
 | 
						|
        global $user, $database, $page;
 | 
						|
 | 
						|
        switch ($event->action) {
 | 
						|
            case self::ACTION_BULK_TRANSCODE:
 | 
						|
                if (!isset($_POST['transcode_mime'])) {
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
                if ($user->can(Permissions::EDIT_FILES)) {
 | 
						|
                    $mime = $_POST['transcode_mime'];
 | 
						|
                    $total = 0;
 | 
						|
                    $size_difference = 0;
 | 
						|
                    foreach ($event->items as $image) {
 | 
						|
                        try {
 | 
						|
                            $database->begin_transaction();
 | 
						|
 | 
						|
                            $before_size =  $image->filesize;
 | 
						|
 | 
						|
                            $new_image = $this->transcode_and_replace_image($image, $mime);
 | 
						|
                            // If a subsequent transcode fails, the database needs to have everything about the previous
 | 
						|
                            // transcodes recorded already, otherwise the image entries will be stuck pointing to
 | 
						|
                            // missing image files
 | 
						|
                            $database->commit();
 | 
						|
                            $total++;
 | 
						|
                            $size_difference += ($before_size - $new_image->filesize);
 | 
						|
                        } catch (Exception $e) {
 | 
						|
                            log_error("transcode", "Error while bulk transcode on item {$image->id} to $mime: ".$e->getMessage());
 | 
						|
                            try {
 | 
						|
                                $database->rollback();
 | 
						|
                            } catch (Exception $e) {
 | 
						|
                                // is this safe? o.o
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    if ($size_difference>0) {
 | 
						|
                        $page->flash("Transcoded $total items, reduced size by ".human_filesize($size_difference));
 | 
						|
                    } elseif ($size_difference<0) {
 | 
						|
                        $page->flash("Transcoded $total items, increased size by ".human_filesize(-1*$size_difference));
 | 
						|
                    } else {
 | 
						|
                        $page->flash("Transcoded $total items, no size difference");
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    private function can_convert_mime($engine, $mime): bool
 | 
						|
    {
 | 
						|
        return MediaEngine::is_input_supported($engine, $mime);
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    private function get_supported_output_mimes($engine, ?String $omit_mime = null): array
 | 
						|
    {
 | 
						|
        $output = [];
 | 
						|
 | 
						|
 | 
						|
        foreach (self::OUTPUT_MIMES as $key=> $value) {
 | 
						|
            if ($value=="") {
 | 
						|
                $output[$key] = $value;
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            if (MediaEngine::is_output_supported($engine, $value)
 | 
						|
                &&(empty($omit_mime)||$omit_mime!=$value)) {
 | 
						|
                $output[$key] = $value;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return $output;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
 | 
						|
    private function transcode_and_replace_image(Image $image_obj, String $target_mime): Image
 | 
						|
    {
 | 
						|
        $original_file = warehouse_path(Image::IMAGE_DIR, $image_obj->hash);
 | 
						|
 | 
						|
        $tmp_filename = $this->transcode_image($original_file, $image_obj->get_mime(), $target_mime);
 | 
						|
 | 
						|
        $new_image = new Image();
 | 
						|
        $new_image->hash = md5_file($tmp_filename);
 | 
						|
        $new_image->filesize = filesize($tmp_filename);
 | 
						|
        $new_image->filename = $image_obj->filename;
 | 
						|
        $new_image->width = $image_obj->width;
 | 
						|
        $new_image->height = $image_obj->height;
 | 
						|
 | 
						|
        /* Move the new image into the main storage location */
 | 
						|
        $target = warehouse_path(Image::IMAGE_DIR, $new_image->hash);
 | 
						|
        if (!@copy($tmp_filename, $target)) {
 | 
						|
            throw new ImageTranscodeException("Failed to copy new image file from temporary location ({$tmp_filename}) to archive ($target)");
 | 
						|
        }
 | 
						|
 | 
						|
        /* Remove temporary file */
 | 
						|
        @unlink($tmp_filename);
 | 
						|
 | 
						|
        send_event(new ImageReplaceEvent($image_obj->id, $new_image));
 | 
						|
 | 
						|
        return $new_image;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    private function transcode_image(String $source_name, String $source_mime, string $target_mime): string
 | 
						|
    {
 | 
						|
        global $config;
 | 
						|
 | 
						|
        if ($source_mime==$target_mime) {
 | 
						|
            throw new ImageTranscodeException("Source and target MIMEs are the same: ".$source_mime);
 | 
						|
        }
 | 
						|
 | 
						|
        $engine = $config->get_string(TranscodeConfig::ENGINE);
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        if (!$this->can_convert_mime($engine, $source_mime)) {
 | 
						|
            throw new ImageTranscodeException("Engine $engine does not support input MIME $source_mime");
 | 
						|
        }
 | 
						|
        if (!MediaEngine::is_output_supported($engine, $target_mime)) {
 | 
						|
            throw new ImageTranscodeException("Engine $engine does not support output MIME $target_mime");
 | 
						|
        }
 | 
						|
 | 
						|
        switch ($engine) {
 | 
						|
            case "gd":
 | 
						|
                return $this->transcode_image_gd($source_name, $source_mime, $target_mime);
 | 
						|
            case "convert":
 | 
						|
                return $this->transcode_image_convert($source_name, $source_mime, $target_mime);
 | 
						|
            default:
 | 
						|
                throw new ImageTranscodeException("No engine specified");
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    private function transcode_image_gd(String $source_name, String $source_mime, string $target_mime): string
 | 
						|
    {
 | 
						|
        global $config;
 | 
						|
 | 
						|
        $q = $config->get_int(TranscodeConfig::QUALITY);
 | 
						|
 | 
						|
        $tmp_name = tempnam(sys_get_temp_dir(), "shimmie_transcode");
 | 
						|
 | 
						|
        $image = imagecreatefromstring(file_get_contents($source_name));
 | 
						|
        try {
 | 
						|
            $result = false;
 | 
						|
            switch ($target_mime) {
 | 
						|
                case MimeType::WEBP:
 | 
						|
                    $result = imagewebp($image, $tmp_name, $q);
 | 
						|
                    break;
 | 
						|
                case MimeType::PNG:
 | 
						|
                    $result = imagepng($image, $tmp_name, 9);
 | 
						|
                    break;
 | 
						|
                case MimeType::JPEG:
 | 
						|
                    // In case of alpha channels
 | 
						|
                    $width = imagesx($image);
 | 
						|
                    $height = imagesy($image);
 | 
						|
                    $new_image = imagecreatetruecolor($width, $height);
 | 
						|
                    if ($new_image===false) {
 | 
						|
                        throw new ImageTranscodeException("Could not create image with dimensions $width x $height");
 | 
						|
                    }
 | 
						|
                    try {
 | 
						|
                        $background_color = Media::hex_color_allocate($new_image, $config->get_string(TranscodeConfig::ALPHA_COLOR));
 | 
						|
                        if ($background_color===false) {
 | 
						|
                            throw new ImageTranscodeException("Could not allocate background color");
 | 
						|
                        }
 | 
						|
                        if (imagefilledrectangle($new_image, 0, 0, $width, $height, $background_color)===false) {
 | 
						|
                            throw new ImageTranscodeException("Could not fill background color");
 | 
						|
                        }
 | 
						|
                        if (imagecopy($new_image, $image, 0, 0, 0, 0, $width, $height)===false) {
 | 
						|
                            throw new ImageTranscodeException("Could not copy source image to new image");
 | 
						|
                        }
 | 
						|
                        $result = imagejpeg($new_image, $tmp_name, $q);
 | 
						|
                    } finally {
 | 
						|
                        imagedestroy($new_image);
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
            }
 | 
						|
        } finally {
 | 
						|
            imagedestroy($image);
 | 
						|
        }
 | 
						|
        if ($result===false) {
 | 
						|
            throw new ImageTranscodeException("Error while transcoding ".$source_name." to ".$target_mime);
 | 
						|
        }
 | 
						|
        return $tmp_name;
 | 
						|
    }
 | 
						|
 | 
						|
    private function transcode_image_convert(String $source_name, String $source_mime, string $target_mime): string
 | 
						|
    {
 | 
						|
        global $config;
 | 
						|
 | 
						|
        $q = $config->get_int(TranscodeConfig::QUALITY);
 | 
						|
        $convert = $config->get_string(MediaConfig::CONVERT_PATH);
 | 
						|
 | 
						|
        if (empty($convert)) {
 | 
						|
            throw new ImageTranscodeException("ImageMagick path not configured");
 | 
						|
        }
 | 
						|
        $ext = Media::determine_ext($target_mime);
 | 
						|
 | 
						|
        $args = " -background ";
 | 
						|
 | 
						|
        if (Media::supports_alpha($target_mime)) {
 | 
						|
            $args .= "none ";
 | 
						|
        } else {
 | 
						|
            $args .= "\"".$config->get_string(TranscodeConfig::ALPHA_COLOR)."\" ";
 | 
						|
        }
 | 
						|
        $args .= " -flatten ";
 | 
						|
 | 
						|
        switch ($target_mime) {
 | 
						|
            case MimeType::PNG:
 | 
						|
                $args .= ' -define png:compression-level=9';
 | 
						|
                break;
 | 
						|
            case MimeType::WEBP_LOSSLESS:
 | 
						|
                $args .= ' -define webp:lossless=true -quality 100 ';
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                $args .= ' -quality '.$q;
 | 
						|
                break;
 | 
						|
        }
 | 
						|
 | 
						|
        $tmp_name = tempnam(sys_get_temp_dir(), "shimmie_transcode");
 | 
						|
 | 
						|
        $source_type = FileExtension::get_for_mime($source_mime);
 | 
						|
 | 
						|
        $format = '"%s" %s:"%s" %s %s:"%s" 2>&1';
 | 
						|
        $cmd = sprintf($format, $convert, $source_type, $source_name, $args, $ext, $tmp_name);
 | 
						|
 | 
						|
        $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);
 | 
						|
 | 
						|
        log_debug('transcode', "Transcoding with command `$cmd`, returns $ret");
 | 
						|
 | 
						|
        if ($ret!==0) {
 | 
						|
            throw new ImageTranscodeException("Transcoding failed with command ".$cmd.", returning ".implode("\r\n", $output));
 | 
						|
        }
 | 
						|
 | 
						|
        return $tmp_name;
 | 
						|
    }
 | 
						|
}
 |