The great MIMEing
This commit is contained in:
parent
8dd5ad16f3
commit
984c9702ec
73 changed files with 1386 additions and 1148 deletions
|
@ -23,7 +23,7 @@ class BasePage
|
|||
/** @var string */
|
||||
public $mode = PageMode::PAGE;
|
||||
/** @var string */
|
||||
private $type = "text/html; charset=utf-8";
|
||||
private $mime;
|
||||
|
||||
/**
|
||||
* Set what this page should do; "page", "data", or "redirect".
|
||||
|
@ -36,13 +36,14 @@ class BasePage
|
|||
/**
|
||||
* Set the page's MIME type.
|
||||
*/
|
||||
public function set_type(string $type): void
|
||||
public function set_mime(string $mime): void
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->mime = $mime;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->mime = MimeType::add_parameters(MimeType::HTML, MimeType::CHARSET_UTF8);
|
||||
if (@$_GET["flash"]) {
|
||||
$this->flash[] = $_GET['flash'];
|
||||
unset($_GET["flash"]);
|
||||
|
@ -243,7 +244,7 @@ class BasePage
|
|||
{
|
||||
if (!headers_sent()) {
|
||||
header("HTTP/1.0 {$this->code} Shimmie");
|
||||
header("Content-type: " . $this->type);
|
||||
header("Content-type: " . $this->mime);
|
||||
header("X-Powered-By: Shimmie-" . VERSION);
|
||||
|
||||
foreach ($this->http_headers as $head) {
|
||||
|
|
|
@ -53,8 +53,9 @@ class BaseThemelet
|
|||
$h_tip = html_escape($image->get_tooltip());
|
||||
$h_tags = html_escape(strtolower($image->get_tag_list()));
|
||||
|
||||
$extArr = array_flip([EXTENSION_FLASH, EXTENSION_SVG, EXTENSION_MP3]); //List of thumbless filetypes
|
||||
if (!isset($extArr[$image->ext])) {
|
||||
// TODO: Set up a function for fetching what kind of files are currently thumbnailable
|
||||
$mimeArr = array_flip([MimeType::MP3]); //List of thumbless filetypes
|
||||
if (!isset($mimeArr[$image->get_mime()])) {
|
||||
$tsize = get_thumbnail_size($image->width, $image->height);
|
||||
} else {
|
||||
//Use max thumbnail size if using thumbless filetype
|
||||
|
|
|
@ -291,11 +291,11 @@ abstract class DataHandlerExtension extends Extension
|
|||
|
||||
public function onDataUpload(DataUploadEvent $event)
|
||||
{
|
||||
$supported_ext = $this->supported_ext($event->type);
|
||||
$supported_mime = $this->supported_mime($event->mime);
|
||||
$check_contents = $this->check_contents($event->tmpname);
|
||||
if ($supported_ext && $check_contents) {
|
||||
if ($supported_mime && $check_contents) {
|
||||
$this->move_upload_to_archive($event);
|
||||
send_event(new ThumbnailGenerationEvent($event->hash, $event->type));
|
||||
send_event(new ThumbnailGenerationEvent($event->hash, $event->mime));
|
||||
|
||||
/* Check if we are replacing an image */
|
||||
if (!is_null($event->replace_id)) {
|
||||
|
@ -317,8 +317,8 @@ abstract class DataHandlerExtension extends Extension
|
|||
if (is_null($image)) {
|
||||
throw new UploadException("Data handler failed to create image object from data");
|
||||
}
|
||||
if (empty($image->ext)) {
|
||||
throw new UploadException("Unable to determine extension for ". $event->tmpname);
|
||||
if (empty($image->get_mime())) {
|
||||
throw new UploadException("Unable to determine MIME for ". $event->tmpname);
|
||||
}
|
||||
try {
|
||||
send_event(new MediaCheckPropertiesEvent($image));
|
||||
|
@ -333,8 +333,8 @@ abstract class DataHandlerExtension extends Extension
|
|||
if (is_null($image)) {
|
||||
throw new UploadException("Data handler failed to create image object from data");
|
||||
}
|
||||
if (empty($image->ext)) {
|
||||
throw new UploadException("Unable to determine extension for ". $event->tmpname);
|
||||
if (empty($image->get_mime())) {
|
||||
throw new UploadException("Unable to determine MIME for ". $event->tmpname);
|
||||
}
|
||||
try {
|
||||
send_event(new MediaCheckPropertiesEvent($image));
|
||||
|
@ -358,7 +358,7 @@ abstract class DataHandlerExtension extends Extension
|
|||
send_event(new LockSetEvent($image, !empty($locked)));
|
||||
}
|
||||
}
|
||||
} elseif ($supported_ext && !$check_contents) {
|
||||
} elseif ($supported_mime && !$check_contents) {
|
||||
// We DO support this extension - but the file looks corrupt
|
||||
throw new UploadException("Invalid or corrupted file");
|
||||
}
|
||||
|
@ -367,15 +367,15 @@ abstract class DataHandlerExtension extends Extension
|
|||
public function onThumbnailGeneration(ThumbnailGenerationEvent $event)
|
||||
{
|
||||
$result = false;
|
||||
if ($this->supported_ext($event->type)) {
|
||||
if ($this->supported_mime($event->mime)) {
|
||||
if ($event->force) {
|
||||
$result = $this->create_thumb($event->hash, $event->type);
|
||||
$result = $this->create_thumb($event->hash, $event->mime);
|
||||
} else {
|
||||
$outname = warehouse_path(Image::THUMBNAIL_DIR, $event->hash);
|
||||
if (file_exists($outname)) {
|
||||
return;
|
||||
}
|
||||
$result = $this->create_thumb($event->hash, $event->type);
|
||||
$result = $this->create_thumb($event->hash, $event->mime);
|
||||
}
|
||||
}
|
||||
if ($result) {
|
||||
|
@ -386,7 +386,7 @@ abstract class DataHandlerExtension extends Extension
|
|||
public function onDisplayingImage(DisplayingImageEvent $event)
|
||||
{
|
||||
global $page;
|
||||
if ($this->supported_ext($event->image->ext)) {
|
||||
if ($this->supported_mime($event->image->get_mime())) {
|
||||
/** @noinspection PhpPossiblePolymorphicInvocationInspection */
|
||||
$this->theme->display_image($page, $event->image);
|
||||
}
|
||||
|
@ -394,7 +394,7 @@ abstract class DataHandlerExtension extends Extension
|
|||
|
||||
public function onMediaCheckProperties(MediaCheckPropertiesEvent $event)
|
||||
{
|
||||
if ($this->supported_ext($event->ext)) {
|
||||
if ($this->supported_mime($event->mime)) {
|
||||
$this->media_check_properties($event);
|
||||
}
|
||||
}
|
||||
|
@ -408,11 +408,11 @@ abstract class DataHandlerExtension extends Extension
|
|||
$image->filesize = $metadata['size'];
|
||||
$image->hash = $metadata['hash'];
|
||||
$image->filename = (($pos = strpos($metadata['filename'], '?')) !== false) ? substr($metadata['filename'], 0, $pos) : $metadata['filename'];
|
||||
if ($config->get_bool("upload_use_mime")) {
|
||||
$image->ext = get_extension_for_file($filename);
|
||||
}
|
||||
if (empty($image->ext)) {
|
||||
$image->ext = (($pos = strpos($metadata['extension'], '?')) !== false) ? substr($metadata['extension'], 0, $pos) : $metadata['extension'];
|
||||
|
||||
if (array_key_exists("extension", $metadata)) {
|
||||
$image->set_mime(MimeType::get_for_file($filename, $metadata["extension"]));
|
||||
} else {
|
||||
$image->set_mime(MimeType::get_for_file($filename));
|
||||
}
|
||||
|
||||
$image->tag_array = is_array($metadata['tags']) ? $metadata['tags'] : Tag::explode($metadata['tags']);
|
||||
|
@ -423,22 +423,35 @@ abstract class DataHandlerExtension extends Extension
|
|||
|
||||
abstract protected function media_check_properties(MediaCheckPropertiesEvent $event): void;
|
||||
abstract protected function check_contents(string $tmpname): bool;
|
||||
abstract protected function create_thumb(string $hash, string $type): bool;
|
||||
abstract protected function create_thumb(string $hash, string $mime): bool;
|
||||
|
||||
protected function supported_ext(string $ext): bool
|
||||
protected function supported_mime(string $mime): bool
|
||||
{
|
||||
return in_array(get_mime_for_extension($ext), $this->SUPPORTED_MIME);
|
||||
return MimeType::matches_array($mime, $this->SUPPORTED_MIME);
|
||||
}
|
||||
|
||||
public static function get_all_supported_mimes(): array
|
||||
{
|
||||
$arr = [];
|
||||
foreach (getSubclassesOf("DataHandlerExtension") as $handler) {
|
||||
$handler = (new $handler());
|
||||
$arr = array_merge($arr, $handler->SUPPORTED_MIME);
|
||||
}
|
||||
|
||||
// Not sure how to handle this otherwise, don't want to set up a whole other event for this one class
|
||||
if (class_exists("TranscodeImage")) {
|
||||
$arr = array_merge($arr, TranscodeImage::get_enabled_mimes());
|
||||
}
|
||||
|
||||
$arr = array_unique($arr);
|
||||
return $arr;
|
||||
}
|
||||
|
||||
public static function get_all_supported_exts(): array
|
||||
{
|
||||
$arr = [];
|
||||
foreach (getSubclassesOf("DataHandlerExtension") as $handler) {
|
||||
$handler = (new $handler());
|
||||
|
||||
foreach ($handler->SUPPORTED_MIME as $mime) {
|
||||
$arr = array_merge($arr, get_all_extension_for_mime($mime));
|
||||
}
|
||||
foreach (self::get_all_supported_mimes() as $mime) {
|
||||
$arr = array_merge($arr, FileExtension::get_all_for_mime($mime));
|
||||
}
|
||||
$arr = array_unique($arr);
|
||||
return $arr;
|
||||
|
|
|
@ -1,448 +0,0 @@
|
|||
<?php
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
|
||||
* MIME types and extension information and resolvers *
|
||||
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
const EXTENSION_ANI = 'ani';
|
||||
const EXTENSION_ASC = 'asc';
|
||||
const EXTENSION_ASF = 'asf';
|
||||
const EXTENSION_AVI = 'avi';
|
||||
const EXTENSION_BMP = 'bmp';
|
||||
const EXTENSION_BZIP = 'bz';
|
||||
const EXTENSION_BZIP2 = 'bz2';
|
||||
const EXTENSION_CBR = 'cbr';
|
||||
const EXTENSION_CBZ = 'cbz';
|
||||
const EXTENSION_CBT = 'cbt';
|
||||
const EXTENSION_CBA = 'cbA';
|
||||
const EXTENSION_CB7 = 'cb7';
|
||||
const EXTENSION_CSS = 'css';
|
||||
const EXTENSION_CSV = 'csv';
|
||||
const EXTENSION_CUR = 'cur';
|
||||
const EXTENSION_FLASH = 'swf';
|
||||
const EXTENSION_FLASH_VIDEO = 'flv';
|
||||
const EXTENSION_GIF = 'gif';
|
||||
const EXTENSION_GZIP = 'gz';
|
||||
const EXTENSION_HTML = 'html';
|
||||
const EXTENSION_HTM = 'htm';
|
||||
const EXTENSION_ICO = 'ico';
|
||||
const EXTENSION_JFIF = 'jfif';
|
||||
const EXTENSION_JFI = 'jfi';
|
||||
const EXTENSION_JPEG = 'jpeg';
|
||||
const EXTENSION_JPG = 'jpg';
|
||||
const EXTENSION_JS = 'js';
|
||||
const EXTENSION_JSON = 'json';
|
||||
const EXTENSION_MKV = 'mkv';
|
||||
const EXTENSION_MP3 = 'mp3';
|
||||
const EXTENSION_MP4 = 'mp4';
|
||||
const EXTENSION_M4V = 'm4v';
|
||||
const EXTENSION_M4A = 'm4a';
|
||||
const EXTENSION_MPEG = 'mpeg';
|
||||
const EXTENSION_MPG = 'mpg';
|
||||
const EXTENSION_OGG = 'ogg';
|
||||
const EXTENSION_OGG_VIDEO = 'ogv';
|
||||
const EXTENSION_OGG_AUDIO = 'oga';
|
||||
const EXTENSION_PDF = 'pdf';
|
||||
const EXTENSION_PHP = 'php';
|
||||
const EXTENSION_PHP5 = 'php5';
|
||||
const EXTENSION_PNG = 'png';
|
||||
const EXTENSION_PSD = 'psd';
|
||||
const EXTENSION_MOV = 'mov';
|
||||
const EXTENSION_RSS = 'rss';
|
||||
const EXTENSION_SVG = 'svg';
|
||||
const EXTENSION_TAR = 'tar';
|
||||
const EXTENSION_TEXT = 'txt';
|
||||
const EXTENSION_TIFF = 'tiff';
|
||||
const EXTENSION_TIF = 'tif';
|
||||
const EXTENSION_WAV = 'wav';
|
||||
const EXTENSION_WEBM = 'webm';
|
||||
const EXTENSION_WEBP = 'webp';
|
||||
const EXTENSION_WMA = 'wma';
|
||||
const EXTENSION_WMV = 'wmv';
|
||||
const EXTENSION_XML = 'xml';
|
||||
const EXTENSION_XSL = 'xsl';
|
||||
const EXTENSION_ZIP = 'zip';
|
||||
|
||||
|
||||
// Couldn't find a mimetype for ani, so made one up based on it being a riff container
|
||||
const MIME_TYPE_ANI = 'application/riff+ani';
|
||||
const MIME_TYPE_ASF = 'video/x-ms-asf';
|
||||
const MIME_TYPE_AVI = 'video/x-msvideo';
|
||||
// Went with mime types from http://fileformats.archiveteam.org/wiki/Comic_Book_Archive
|
||||
const MIME_TYPE_COMIC_ZIP = 'application/vnd.comicbook+zip';
|
||||
const MIME_TYPE_COMIC_RAR = 'application/vnd.comicbook-rar';
|
||||
const MIME_TYPE_BMP = 'image/x-ms-bmp';
|
||||
const MIME_TYPE_BZIP = 'application/x-bzip';
|
||||
const MIME_TYPE_BZIP2 = 'application/x-bzip2';
|
||||
const MIME_TYPE_CSS = 'text/css';
|
||||
const MIME_TYPE_CSV = 'text/csv';
|
||||
const MIME_TYPE_FLASH = 'application/x-shockwave-flash';
|
||||
const MIME_TYPE_FLASH_VIDEO = 'video/x-flv';
|
||||
const MIME_TYPE_GIF = 'image/gif';
|
||||
const MIME_TYPE_GZIP = 'application/x-gzip';
|
||||
const MIME_TYPE_HTML = 'text/html';
|
||||
const MIME_TYPE_ICO = 'image/x-icon';
|
||||
const MIME_TYPE_JPEG = 'image/jpeg';
|
||||
const MIME_TYPE_JS = 'text/javascript';
|
||||
const MIME_TYPE_JSON = 'application/json';
|
||||
const MIME_TYPE_MKV = 'video/x-matroska';
|
||||
const MIME_TYPE_MP3 = 'audio/mpeg';
|
||||
const MIME_TYPE_MP4_AUDIO = 'audio/mp4';
|
||||
const MIME_TYPE_MP4_VIDEO = 'video/mp4';
|
||||
const MIME_TYPE_MPEG = 'video/mpeg';
|
||||
const MIME_TYPE_OCTET_STREAM = 'application/octet-stream';
|
||||
const MIME_TYPE_OGG = 'application/ogg';
|
||||
const MIME_TYPE_OGG_VIDEO = 'video/ogg';
|
||||
const MIME_TYPE_OGG_AUDIO = 'audio/ogg';
|
||||
const MIME_TYPE_PDF = 'application/pdf';
|
||||
const MIME_TYPE_PHP = 'text/x-php';
|
||||
const MIME_TYPE_PNG = 'image/png';
|
||||
const MIME_TYPE_PSD = 'image/vnd.adobe.photoshop';
|
||||
const MIME_TYPE_QUICKTIME = 'video/quicktime';
|
||||
const MIME_TYPE_RSS = 'application/rss+xml';
|
||||
const MIME_TYPE_SVG = 'image/svg+xml';
|
||||
const MIME_TYPE_TAR = 'application/x-tar';
|
||||
const MIME_TYPE_TEXT = 'text/plain';
|
||||
const MIME_TYPE_TIFF = 'image/tiff';
|
||||
const MIME_TYPE_WAV = 'audio/x-wav';
|
||||
const MIME_TYPE_WEBM = 'video/webm';
|
||||
const MIME_TYPE_WEBP = 'image/webp';
|
||||
const MIME_TYPE_WIN_BITMAP = 'image/x-win-bitmap';
|
||||
const MIME_TYPE_XML = 'text/xml';
|
||||
const MIME_TYPE_XML_APPLICATION = 'application/xml';
|
||||
const MIME_TYPE_XSL = 'application/xsl+xml';
|
||||
const MIME_TYPE_ZIP = 'application/zip';
|
||||
|
||||
const MIME_TYPE_MAP_NAME = 'name';
|
||||
const MIME_TYPE_MAP_EXT = 'ext';
|
||||
const MIME_TYPE_MAP_MIME = 'mime';
|
||||
|
||||
// Mime type map. Each entry in the MIME_TYPE_ARRAY represents a kind of file, identified by the "correct" mimetype as the key.
|
||||
// The value for each entry is a map of twokeys, ext and mime.
|
||||
// ext's value is an array of all of the extensions that the file type can use, with the "correct" one being first.
|
||||
// mime's value is an array of all mime types that the file type is known to use, with the current "correct" one being first.
|
||||
|
||||
const MIME_TYPE_MAP = [
|
||||
MIME_TYPE_ANI => [
|
||||
MIME_TYPE_MAP_NAME => "ANI Cursor",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_ANI],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_ANI],
|
||||
],
|
||||
MIME_TYPE_AVI => [
|
||||
MIME_TYPE_MAP_NAME => "AVI",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_AVI],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_AVI,'video/avi','video/msvideo'],
|
||||
],
|
||||
MIME_TYPE_ASF => [
|
||||
MIME_TYPE_MAP_NAME => "ASF/WMV",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_ASF,EXTENSION_WMA,EXTENSION_WMV],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_ASF,'audio/x-ms-wma','video/x-ms-wmv'],
|
||||
],
|
||||
MIME_TYPE_BMP => [
|
||||
MIME_TYPE_MAP_NAME => "BMP",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_BMP],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_BMP],
|
||||
],
|
||||
MIME_TYPE_BZIP => [
|
||||
MIME_TYPE_MAP_NAME => "BZIP",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_BZIP],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_BZIP],
|
||||
],
|
||||
MIME_TYPE_BZIP2 => [
|
||||
MIME_TYPE_MAP_NAME => "BZIP2",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_BZIP2],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_BZIP2],
|
||||
],
|
||||
MIME_TYPE_COMIC_ZIP => [
|
||||
MIME_TYPE_MAP_NAME => "CBZ",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_CBZ],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_COMIC_ZIP],
|
||||
],
|
||||
MIME_TYPE_CSS => [
|
||||
MIME_TYPE_MAP_NAME => "Cascading Style Sheet",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_CSS],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_CSS],
|
||||
],
|
||||
MIME_TYPE_CSV => [
|
||||
MIME_TYPE_MAP_NAME => "CSV",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_CSV],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_CSV],
|
||||
],
|
||||
MIME_TYPE_FLASH => [
|
||||
MIME_TYPE_MAP_NAME => "Flash",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_FLASH],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_FLASH],
|
||||
],
|
||||
MIME_TYPE_FLASH_VIDEO => [
|
||||
MIME_TYPE_MAP_NAME => "Flash Video",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_FLASH_VIDEO],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_FLASH_VIDEO,'video/flv'],
|
||||
],
|
||||
MIME_TYPE_GIF => [
|
||||
MIME_TYPE_MAP_NAME => "GIF",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_GIF],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_GIF],
|
||||
],
|
||||
MIME_TYPE_GZIP => [
|
||||
MIME_TYPE_MAP_NAME => "GZIP",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_GZIP],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_TAR],
|
||||
],
|
||||
MIME_TYPE_HTML => [
|
||||
MIME_TYPE_MAP_NAME => "HTML",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_HTM, EXTENSION_HTML],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_HTML],
|
||||
],
|
||||
MIME_TYPE_ICO => [
|
||||
MIME_TYPE_MAP_NAME => "Icon",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_ICO, EXTENSION_CUR],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_ICO, MIME_TYPE_WIN_BITMAP],
|
||||
],
|
||||
MIME_TYPE_JPEG => [
|
||||
MIME_TYPE_MAP_NAME => "JPEG",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_JPG, EXTENSION_JPEG, EXTENSION_JFIF, EXTENSION_JFI],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_JPEG],
|
||||
],
|
||||
MIME_TYPE_JS => [
|
||||
MIME_TYPE_MAP_NAME => "JavaScript",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_JS],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_JS],
|
||||
],
|
||||
MIME_TYPE_JSON => [
|
||||
MIME_TYPE_MAP_NAME => "JSON",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_JSON],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_JSON],
|
||||
],
|
||||
MIME_TYPE_MKV => [
|
||||
MIME_TYPE_MAP_NAME => "Matroska",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_MKV],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_MKV],
|
||||
],
|
||||
MIME_TYPE_MP3 => [
|
||||
MIME_TYPE_MAP_NAME => "MP3",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_MP3],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_MP3],
|
||||
],
|
||||
MIME_TYPE_MP4_AUDIO => [
|
||||
MIME_TYPE_MAP_NAME => "MP4 Audio",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_M4A],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_MP4_AUDIO,"audio/m4a"],
|
||||
],
|
||||
MIME_TYPE_MP4_VIDEO => [
|
||||
MIME_TYPE_MAP_NAME => "MP4 Video",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_MP4,EXTENSION_M4V],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_MP4_VIDEO,'video/x-m4v'],
|
||||
],
|
||||
MIME_TYPE_MPEG => [
|
||||
MIME_TYPE_MAP_NAME => "MPEG",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_MPG,EXTENSION_MPEG],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_MPEG],
|
||||
],
|
||||
MIME_TYPE_PDF => [
|
||||
MIME_TYPE_MAP_NAME => "PDF",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_PDF],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_PDF],
|
||||
],
|
||||
MIME_TYPE_PHP => [
|
||||
MIME_TYPE_MAP_NAME => "PHP",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_PHP,EXTENSION_PHP5],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_PHP],
|
||||
],
|
||||
MIME_TYPE_PNG => [
|
||||
MIME_TYPE_MAP_NAME => "PNG",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_PNG],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_PNG],
|
||||
],
|
||||
MIME_TYPE_PSD => [
|
||||
MIME_TYPE_MAP_NAME => "PSD",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_PSD],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_PSD],
|
||||
],
|
||||
MIME_TYPE_OGG_AUDIO => [
|
||||
MIME_TYPE_MAP_NAME => "Ogg Vorbis",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_OGG_AUDIO,EXTENSION_OGG],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_OGG_AUDIO,MIME_TYPE_OGG],
|
||||
],
|
||||
MIME_TYPE_OGG_VIDEO => [
|
||||
MIME_TYPE_MAP_NAME => "Ogg Theora",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_OGG_VIDEO],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_OGG_VIDEO],
|
||||
],
|
||||
MIME_TYPE_QUICKTIME => [
|
||||
MIME_TYPE_MAP_NAME => "Quicktime",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_MOV],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_QUICKTIME],
|
||||
],
|
||||
MIME_TYPE_RSS => [
|
||||
MIME_TYPE_MAP_NAME => "RSS",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_RSS],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_RSS],
|
||||
],
|
||||
MIME_TYPE_SVG => [
|
||||
MIME_TYPE_MAP_NAME => "SVG",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_SVG],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_SVG],
|
||||
],
|
||||
MIME_TYPE_TAR => [
|
||||
MIME_TYPE_MAP_NAME => "TAR",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_TAR],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_TAR],
|
||||
],
|
||||
MIME_TYPE_TEXT => [
|
||||
MIME_TYPE_MAP_NAME => "Text",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_TEXT, EXTENSION_ASC],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_TEXT],
|
||||
],
|
||||
MIME_TYPE_TIFF => [
|
||||
MIME_TYPE_MAP_NAME => "TIFF",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_TIF,EXTENSION_TIFF],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_TIFF],
|
||||
],
|
||||
MIME_TYPE_WAV => [
|
||||
MIME_TYPE_MAP_NAME => "Wave",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_WAV],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_WAV],
|
||||
],
|
||||
MIME_TYPE_WEBM => [
|
||||
MIME_TYPE_MAP_NAME => "WebM",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_WEBM],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_WEBM],
|
||||
],
|
||||
MIME_TYPE_WEBP => [
|
||||
MIME_TYPE_MAP_NAME => "WebP",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_WEBP],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_WEBP],
|
||||
],
|
||||
MIME_TYPE_XML => [
|
||||
MIME_TYPE_MAP_NAME => "XML",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_XML],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_XML,MIME_TYPE_XML_APPLICATION],
|
||||
],
|
||||
MIME_TYPE_XSL => [
|
||||
MIME_TYPE_MAP_NAME => "XSL",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_XSL],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_XSL],
|
||||
],
|
||||
MIME_TYPE_ZIP => [
|
||||
MIME_TYPE_MAP_NAME => "ZIP",
|
||||
MIME_TYPE_MAP_EXT => [EXTENSION_ZIP],
|
||||
MIME_TYPE_MAP_MIME => [MIME_TYPE_ZIP],
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns the mimetype that matches the provided extension.
|
||||
*/
|
||||
function get_mime_for_extension(string $ext): ?string
|
||||
{
|
||||
$ext = strtolower($ext);
|
||||
|
||||
foreach (MIME_TYPE_MAP as $key=>$value) {
|
||||
if (in_array($ext, $value[MIME_TYPE_MAP_EXT])) {
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mimetype for the specified file, trying file inspection methods before falling back on extension-based detection.
|
||||
* @param String $file
|
||||
* @param String $ext The files extension, for if the current filename somehow lacks the extension
|
||||
* @return String The extension that was found.
|
||||
*/
|
||||
function get_mime(string $file, string $ext=""): string
|
||||
{
|
||||
if (!file_exists($file)) {
|
||||
throw new SCoreException("File not found: ".$file);
|
||||
}
|
||||
|
||||
$type = false;
|
||||
|
||||
if (extension_loaded('fileinfo')) {
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
try {
|
||||
$type = finfo_file($finfo, $file);
|
||||
} finally {
|
||||
finfo_close($finfo);
|
||||
}
|
||||
} elseif (function_exists('mime_content_type')) {
|
||||
// If anyone is still using mime_content_type()
|
||||
$type = trim(mime_content_type($file));
|
||||
}
|
||||
|
||||
if ($type===false || empty($type)) {
|
||||
// Checking by extension is our last resort
|
||||
if ($ext==null||strlen($ext) == 0) {
|
||||
$ext = pathinfo($file, PATHINFO_EXTENSION);
|
||||
}
|
||||
|
||||
$type = get_mime_for_extension($ext);
|
||||
}
|
||||
|
||||
if ($type !== false && strlen($type) > 0) {
|
||||
return $type;
|
||||
}
|
||||
|
||||
return MIME_TYPE_OCTET_STREAM;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file extension associated with the specified mimetype.
|
||||
*/
|
||||
function get_extension(?string $mime_type): ?string
|
||||
{
|
||||
if (empty($mime_type)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($mime_type==MIME_TYPE_OCTET_STREAM) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (MIME_TYPE_MAP as $key=>$value) {
|
||||
if (in_array($mime_type, $value[MIME_TYPE_MAP_MIME])) {
|
||||
return $value[MIME_TYPE_MAP_EXT][0];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all of the file extensions associated with the specified mimetype.
|
||||
*/
|
||||
function get_all_extension_for_mime(?string $mime_type): array
|
||||
{
|
||||
$output = [];
|
||||
if (empty($mime_type)) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
foreach (MIME_TYPE_MAP as $key=>$value) {
|
||||
if (in_array($mime_type, $value[MIME_TYPE_MAP_MIME])) {
|
||||
$output = array_merge($output, $value[MIME_TYPE_MAP_EXT]);
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an the extension defined in MIME_TYPE_MAP for a file.
|
||||
*
|
||||
* @param String $file_path
|
||||
* @return String The extension that was found, or null if one can not be found.
|
||||
*/
|
||||
function get_extension_for_file(String $file_path): ?String
|
||||
{
|
||||
$mime = get_mime($file_path);
|
||||
if (!empty($mime)) {
|
||||
if ($mime==MIME_TYPE_OCTET_STREAM) {
|
||||
return null;
|
||||
} else {
|
||||
$ext = get_extension($mime);
|
||||
}
|
||||
if (!empty($ext)) {
|
||||
return $ext;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
|
@ -91,7 +91,7 @@ class ThumbnailGenerationEvent extends Event
|
|||
/** @var string */
|
||||
public $hash;
|
||||
/** @var string */
|
||||
public $type;
|
||||
public $mime;
|
||||
/** @var bool */
|
||||
public $force;
|
||||
|
||||
|
@ -101,11 +101,11 @@ class ThumbnailGenerationEvent extends Event
|
|||
/**
|
||||
* Request a thumbnail be made for an image object
|
||||
*/
|
||||
public function __construct(string $hash, string $type, bool $force=false)
|
||||
public function __construct(string $hash, string $mime, bool $force=false)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->hash = $hash;
|
||||
$this->type = $type;
|
||||
$this->mime = $mime;
|
||||
$this->force = $force;
|
||||
$this->generated = false;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,10 @@ class Image
|
|||
public $filename;
|
||||
|
||||
/** @var string */
|
||||
public $ext;
|
||||
private $ext;
|
||||
|
||||
/** @var string */
|
||||
private $mime;
|
||||
|
||||
/** @var string[]|null */
|
||||
public $tag_array;
|
||||
|
@ -396,22 +399,22 @@ class Image
|
|||
"INSERT INTO images(
|
||||
owner_id, owner_ip,
|
||||
filename, filesize,
|
||||
hash, ext,
|
||||
hash, mime, ext,
|
||||
width, height,
|
||||
posted, source
|
||||
)
|
||||
VALUES (
|
||||
:owner_id, :owner_ip,
|
||||
:filename, :filesize,
|
||||
:hash, :ext,
|
||||
:hash, :mime, :ext,
|
||||
0, 0,
|
||||
now(), :source
|
||||
)",
|
||||
[
|
||||
"owner_id" => $user->id, "owner_ip" => $_SERVER['REMOTE_ADDR'],
|
||||
"filename" => $cut_name, "filesize" => $this->filesize,
|
||||
"hash" => $this->hash, "ext" => strtolower($this->ext),
|
||||
"source" => $this->source
|
||||
"hash" => $this->hash, "mime" => strtolower($this->mime),
|
||||
"ext" => strtolower($this->ext), "source" => $this->source
|
||||
]
|
||||
);
|
||||
$this->id = $database->get_last_insert_id('images_id_seq');
|
||||
|
@ -419,12 +422,13 @@ class Image
|
|||
$database->execute(
|
||||
"UPDATE images SET ".
|
||||
"filename = :filename, filesize = :filesize, hash = :hash, ".
|
||||
"ext = :ext, width = 0, height = 0, source = :source ".
|
||||
"mime = :mime, ext = :ext, width = 0, height = 0, source = :source ".
|
||||
"WHERE id = :id",
|
||||
[
|
||||
"filename" => $cut_name,
|
||||
"filesize" => $this->filesize,
|
||||
"hash" => $this->hash,
|
||||
"mime" => strtolower($this->mime),
|
||||
"ext" => strtolower($this->ext),
|
||||
"source" => $this->source,
|
||||
"id" => $this->id,
|
||||
|
@ -503,7 +507,8 @@ class Image
|
|||
public function get_thumb_link(): string
|
||||
{
|
||||
global $config;
|
||||
$ext = $config->get_string(ImageConfig::THUMB_TYPE);
|
||||
$mime = $config->get_string(ImageConfig::THUMB_MIME);
|
||||
$ext = FileExtension::get_for_mime($mime);
|
||||
return $this->get_link(ImageConfig::TLINK, '_thumbs/$hash/thumb.'.$ext, 'thumb/$id.'.$ext);
|
||||
}
|
||||
|
||||
|
@ -566,21 +571,34 @@ class Image
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the image's mime type.
|
||||
*/
|
||||
public function get_mime_type(): string
|
||||
{
|
||||
return get_mime($this->get_image_filename(), $this->get_ext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the image's filename extension
|
||||
* Get the image's extension.
|
||||
*/
|
||||
public function get_ext(): string
|
||||
{
|
||||
return $this->ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the image's mime type.
|
||||
*/
|
||||
public function get_mime(): string
|
||||
{
|
||||
if ($this->mime===MimeType::WEBP&&$this->lossless) {
|
||||
return MimeType::WEBP_LOSSLESS;
|
||||
}
|
||||
return $this->mime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the image's mime type.
|
||||
*/
|
||||
public function set_mime($mime): void
|
||||
{
|
||||
$this->mime = $mime;
|
||||
$this->ext = FileExtension::get_for_mime($this->get_mime());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the image's source URL
|
||||
*/
|
||||
|
|
|
@ -136,7 +136,7 @@ function get_thumbnail_max_size_scaled(): array
|
|||
}
|
||||
|
||||
|
||||
function create_image_thumb(string $hash, string $type, string $engine = null)
|
||||
function create_image_thumb(string $hash, string $mime, string $engine = null)
|
||||
{
|
||||
global $config;
|
||||
|
||||
|
@ -147,7 +147,7 @@ function create_image_thumb(string $hash, string $type, string $engine = null)
|
|||
$inname,
|
||||
$outname,
|
||||
$tsize,
|
||||
$type,
|
||||
$mime,
|
||||
$engine,
|
||||
$config->get_string(ImageConfig::THUMB_FIT)
|
||||
);
|
||||
|
@ -155,7 +155,7 @@ function create_image_thumb(string $hash, string $type, string $engine = null)
|
|||
|
||||
|
||||
|
||||
function create_scaled_image(string $inname, string $outname, array $tsize, string $type, ?string $engine = null, ?string $resize_type = null)
|
||||
function create_scaled_image(string $inname, string $outname, array $tsize, string $mime, ?string $engine = null, ?string $resize_type = null)
|
||||
{
|
||||
global $config;
|
||||
if (empty($engine)) {
|
||||
|
@ -165,20 +165,17 @@ function create_scaled_image(string $inname, string $outname, array $tsize, stri
|
|||
$resize_type = $config->get_string(ImageConfig::THUMB_FIT);
|
||||
}
|
||||
|
||||
$output_format = $config->get_string(ImageConfig::THUMB_TYPE);
|
||||
if ($output_format==EXTENSION_WEBP) {
|
||||
$output_format = Media::WEBP_LOSSY;
|
||||
}
|
||||
$output_mime = $config->get_string(ImageConfig::THUMB_MIME);
|
||||
|
||||
send_event(new MediaResizeEvent(
|
||||
$engine,
|
||||
$inname,
|
||||
$type,
|
||||
$mime,
|
||||
$outname,
|
||||
$tsize[0],
|
||||
$tsize[1],
|
||||
$resize_type,
|
||||
$output_format,
|
||||
$output_mime,
|
||||
$config->get_int(ImageConfig::THUMB_QUALITY),
|
||||
true,
|
||||
true
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
* Things which should be in the core API *
|
||||
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
require_once "filetypes.php";
|
||||
|
||||
|
||||
/**
|
||||
* Return the unique elements of an array, case insensitively
|
||||
*/
|
||||
|
|
|
@ -103,7 +103,7 @@ class AdminPage extends Extension
|
|||
$uid = $event->args[0];
|
||||
$image = Image::by_id_or_hash($uid);
|
||||
if ($image) {
|
||||
send_event(new ThumbnailGenerationEvent($image->hash, $image->ext, true));
|
||||
send_event(new ThumbnailGenerationEvent($image->hash, $image->get_mime(), true));
|
||||
} else {
|
||||
print("No post with ID '$uid'\n");
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ class AliasEditor extends Extension
|
|||
$this->theme->display_aliases($t->table($t->query()), $t->paginator());
|
||||
} elseif ($event->get_arg(0) == "export") {
|
||||
$page->set_mode(PageMode::DATA);
|
||||
$page->set_type(MIME_TYPE_CSV);
|
||||
$page->set_mime(MimeType::CSV);
|
||||
$page->set_filename("aliases.csv");
|
||||
$page->set_data($this->get_alias_csv($database));
|
||||
} elseif ($event->get_arg(0) == "import") {
|
||||
|
|
|
@ -102,7 +102,7 @@ class AutoTagger extends Extension
|
|||
$this->theme->display_auto_tagtable($t->table($t->query()), $t->paginator());
|
||||
} elseif ($event->get_arg(0) == "export") {
|
||||
$page->set_mode(PageMode::DATA);
|
||||
$page->set_type(MIME_TYPE_CSV);
|
||||
$page->set_mime(MimeType::CSV);
|
||||
$page->set_filename("auto_tag.csv");
|
||||
$page->set_data($this->get_auto_tag_csv($database));
|
||||
} elseif ($event->get_arg(0) == "import") {
|
||||
|
|
|
@ -20,7 +20,7 @@ class AutoComplete extends Extension
|
|||
}
|
||||
|
||||
$page->set_mode(PageMode::DATA);
|
||||
$page->set_type(MIME_TYPE_JSON);
|
||||
$page->set_mime(MimeType::JSON);
|
||||
|
||||
$s = strtolower($_GET["s"]);
|
||||
if (
|
||||
|
|
|
@ -42,7 +42,7 @@ class BrowserSearch extends Extension
|
|||
|
||||
// And now to send it to the browser
|
||||
$page->set_mode(PageMode::DATA);
|
||||
$page->set_type(MIME_TYPE_XML);
|
||||
$page->set_mime(MimeType::XML);
|
||||
$page->set_data($xml);
|
||||
} elseif ($event->page_matches("browser_search")) {
|
||||
$suggestions = $config->get_string("search_suggestions_results_order");
|
||||
|
|
|
@ -5,14 +5,14 @@ class BulkImportExport extends DataHandlerExtension
|
|||
{
|
||||
const EXPORT_ACTION_NAME = "bulk_export";
|
||||
const EXPORT_INFO_FILE_NAME = "export.json";
|
||||
protected $SUPPORTED_MIME = [MIME_TYPE_ZIP];
|
||||
protected $SUPPORTED_MIME = [MimeType::ZIP];
|
||||
|
||||
|
||||
public function onDataUpload(DataUploadEvent $event)
|
||||
{
|
||||
global $user, $database;
|
||||
|
||||
if ($this->supported_ext($event->type) &&
|
||||
if ($this->supported_mime($event->mime) &&
|
||||
$user->can(Permissions::BULK_IMPORT)) {
|
||||
$zip = new ZipArchive;
|
||||
|
||||
|
|
|
@ -545,7 +545,7 @@ class CronUploader extends Extension
|
|||
global $page;
|
||||
|
||||
$page->set_mode(PageMode::MANUAL);
|
||||
$page->set_type(MIME_TYPE_TEXT);
|
||||
$page->set_mime(MimeType::TEXT);
|
||||
$page->send_headers();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,13 +10,13 @@ class DanbooruApi extends Extension
|
|||
|
||||
if ($event->page_matches("api/danbooru/add_post") || $event->page_matches("api/danbooru/post/create.xml")) {
|
||||
// No XML data is returned from this function
|
||||
$page->set_type(MIME_TYPE_TEXT);
|
||||
$page->set_mime(MimeType::TEXT);
|
||||
$this->api_add_post();
|
||||
} elseif ($event->page_matches("api/danbooru/find_posts") || $event->page_matches("api/danbooru/post/index.xml")) {
|
||||
$page->set_type(MIME_TYPE_XML_APPLICATION);
|
||||
$page->set_mime(MimeType::XML_APPLICATION);
|
||||
$page->set_data($this->api_find_posts());
|
||||
} elseif ($event->page_matches("api/danbooru/find_tags")) {
|
||||
$page->set_type(MIME_TYPE_XML_APPLICATION);
|
||||
$page->set_mime(MimeType::XML_APPLICATION);
|
||||
$page->set_data($this->api_find_tags());
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ class Download extends Extension
|
|||
{
|
||||
global $page;
|
||||
|
||||
$page->set_type($event->mime);
|
||||
$page->set_mime($event->mime);
|
||||
|
||||
$page->set_mode(PageMode::FILE);
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ class ET extends Extension
|
|||
"extensions" => [
|
||||
"core" => $core_exts,
|
||||
"extra" => $extra_exts,
|
||||
"handled_extensions" => DataHandlerExtension::get_all_supported_exts(),
|
||||
"handled_mimes" => DataHandlerExtension::get_all_supported_mimes(),
|
||||
],
|
||||
"stats" => [
|
||||
'images' => (int)$database->get_one("SELECT COUNT(*) FROM images"),
|
||||
|
@ -94,7 +94,7 @@ class ET extends Extension
|
|||
"width" => $config->get_int(ImageConfig::THUMB_WIDTH),
|
||||
"height" => $config->get_int(ImageConfig::THUMB_HEIGHT),
|
||||
"scaling" => $config->get_int(ImageConfig::THUMB_SCALING),
|
||||
"type" => $config->get_string(ImageConfig::THUMB_TYPE),
|
||||
"mime" => $config->get_string(ImageConfig::THUMB_MIME),
|
||||
],
|
||||
];
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ class Featured extends Extension
|
|||
$image = Image::by_id($config->get_int("featured_id"));
|
||||
if (!is_null($image)) {
|
||||
$page->set_mode(PageMode::DATA);
|
||||
$page->set_type($image->get_mime_type());
|
||||
$page->set_mime($image->get_mime());
|
||||
$page->set_data(file_get_contents($image->get_image_filename()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class ArchiveFileHandler extends DataHandlerExtension
|
||||
{
|
||||
protected $SUPPORTED_MIME = [MIME_TYPE_ZIP];
|
||||
protected $SUPPORTED_MIME = [MimeType::ZIP];
|
||||
|
||||
public function onInitExt(InitExtEvent $event)
|
||||
{
|
||||
|
@ -21,7 +21,7 @@ class ArchiveFileHandler extends DataHandlerExtension
|
|||
|
||||
public function onDataUpload(DataUploadEvent $event)
|
||||
{
|
||||
if ($this->supported_ext($event->type)) {
|
||||
if ($this->supported_mime($event->mime)) {
|
||||
global $config, $page;
|
||||
$tmp = sys_get_temp_dir();
|
||||
$tmpdir = "$tmp/shimmie-archive-{$event->hash}";
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class CBZFileHandler extends DataHandlerExtension
|
||||
{
|
||||
public $SUPPORTED_MIME = [MIME_TYPE_COMIC_ZIP];
|
||||
protected $SUPPORTED_MIME = [MimeType::COMIC_ZIP];
|
||||
|
||||
protected function media_check_properties(MediaCheckPropertiesEvent $event): void
|
||||
{
|
||||
|
@ -20,14 +20,14 @@ class CBZFileHandler extends DataHandlerExtension
|
|||
unlink($tmp);
|
||||
}
|
||||
|
||||
protected function create_thumb(string $hash, string $type): bool
|
||||
protected function create_thumb(string $hash, string $mime): bool
|
||||
{
|
||||
$cover = $this->get_representative_image(warehouse_path(Image::IMAGE_DIR, $hash));
|
||||
create_scaled_image(
|
||||
$cover,
|
||||
warehouse_path(Image::THUMBNAIL_DIR, $hash),
|
||||
get_thumbnail_max_size_scaled(),
|
||||
get_extension(get_mime($cover)),
|
||||
MimeType::get_for_file($cover),
|
||||
null
|
||||
);
|
||||
return true;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class FlashFileHandler extends DataHandlerExtension
|
||||
{
|
||||
protected $SUPPORTED_MIME = [MIME_TYPE_FLASH];
|
||||
protected $SUPPORTED_MIME = [MimeType::FLASH];
|
||||
|
||||
protected function media_check_properties(MediaCheckPropertiesEvent $event): void
|
||||
{
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
|
||||
class IcoFileHandler extends DataHandlerExtension
|
||||
{
|
||||
protected $SUPPORTED_MIME = [MIME_TYPE_ICO, MIME_TYPE_ANI];
|
||||
protected $SUPPORTED_MIME = [MimeType::ICO, MimeType::ANI, MimeType::WIN_BITMAP];
|
||||
|
||||
protected function media_check_properties(MediaCheckPropertiesEvent $event): void
|
||||
{
|
||||
$event->image->lossless = true;
|
||||
$event->image->video = false;
|
||||
$event->image->audio = false;
|
||||
$event->image->image = ($event->ext!="ani");
|
||||
$event->image->image = ($event->mime!= MimeType::ANI);
|
||||
|
||||
$fp = fopen($event->file_name, "r");
|
||||
try {
|
||||
|
@ -25,10 +25,10 @@ class IcoFileHandler extends DataHandlerExtension
|
|||
$event->image->height = $height == 0 ? 256 : $height;
|
||||
}
|
||||
|
||||
protected function create_thumb(string $hash, string $type): bool
|
||||
protected function create_thumb(string $hash, string $mime): bool
|
||||
{
|
||||
try {
|
||||
create_image_thumb($hash, $type, MediaEngine::IMAGICK);
|
||||
create_image_thumb($hash, $mime, MediaEngine::IMAGICK);
|
||||
return true;
|
||||
} catch (MediaException $e) {
|
||||
log_warning("handle_ico", "Could not generate thumbnail. " . $e->getMessage());
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
// TODO: Add support for generating an icon from embedded cover art
|
||||
// TODO: MORE AUDIO FORMATS
|
||||
|
||||
class MP3FileHandler extends DataHandlerExtension
|
||||
{
|
||||
protected $SUPPORTED_MIME = [MIME_TYPE_MP3];
|
||||
protected $SUPPORTED_MIME = [MimeType::MP3];
|
||||
|
||||
protected function media_check_properties(MediaCheckPropertiesEvent $event): void
|
||||
{
|
||||
|
@ -23,6 +26,6 @@ class MP3FileHandler extends DataHandlerExtension
|
|||
|
||||
protected function check_contents(string $tmpname): bool
|
||||
{
|
||||
return get_mime($tmpname) === MIME_TYPE_MP3;
|
||||
return MimeType::get_for_file($tmpname) === MimeType::MP3;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,26 +2,18 @@
|
|||
|
||||
class PixelFileHandler extends DataHandlerExtension
|
||||
{
|
||||
protected $SUPPORTED_MIME = [MIME_TYPE_JPEG, MIME_TYPE_GIF, MIME_TYPE_PNG, MIME_TYPE_WEBP];
|
||||
protected $SUPPORTED_MIME = [MimeType::JPEG, MimeType::GIF, MimeType::PNG, MimeType::WEBP];
|
||||
|
||||
protected function media_check_properties(MediaCheckPropertiesEvent $event): void
|
||||
{
|
||||
if (in_array($event->ext, Media::LOSSLESS_FORMATS)) {
|
||||
$event->image->lossless = true;
|
||||
} elseif ($event->ext==EXTENSION_WEBP) {
|
||||
$event->image->lossless = Media::is_lossless_webp($event->file_name);
|
||||
}
|
||||
|
||||
if ($event->image->lossless==null) {
|
||||
$event->image->lossless = false;
|
||||
}
|
||||
$event->image->lossless = Media::is_lossless($event->file_name, $event->mime);
|
||||
$event->image->audio = false;
|
||||
switch ($event->ext) {
|
||||
case EXTENSION_GIF:
|
||||
$event->image->video = Media::is_animated_gif($event->file_name);
|
||||
switch ($event->mime) {
|
||||
case MimeType::GIF:
|
||||
$event->image->video = MimeType::is_animated_gif($event->file_name);
|
||||
break;
|
||||
case EXTENSION_WEBP:
|
||||
$event->image->video = Media::is_animated_webp($event->file_name);
|
||||
case MimeType::WEBP:
|
||||
$event->image->video = MimeType::is_animated_webp($event->file_name);
|
||||
break;
|
||||
default:
|
||||
$event->image->video = false;
|
||||
|
|
|
@ -3,7 +3,7 @@ use enshrined\svgSanitize\Sanitizer;
|
|||
|
||||
class SVGFileHandler extends DataHandlerExtension
|
||||
{
|
||||
protected $SUPPORTED_MIME = [MIME_TYPE_SVG];
|
||||
protected $SUPPORTED_MIME = [MimeType::SVG];
|
||||
|
||||
/** @var SVGFileHandlerTheme */
|
||||
protected $theme;
|
||||
|
@ -16,7 +16,7 @@ class SVGFileHandler extends DataHandlerExtension
|
|||
$image = Image::by_id($id);
|
||||
$hash = $image->hash;
|
||||
|
||||
$page->set_type(MIME_TYPE_SVG);
|
||||
$page->set_mime(MimeType::SVG);
|
||||
$page->set_mode(PageMode::DATA);
|
||||
|
||||
$sanitizer = new Sanitizer();
|
||||
|
@ -67,7 +67,7 @@ class SVGFileHandler extends DataHandlerExtension
|
|||
|
||||
protected function check_contents(string $file): bool
|
||||
{
|
||||
if (get_mime($file)!==MIME_TYPE_SVG) {
|
||||
if (MimeType::get_for_file($file)!==MimeType::SVG) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,14 +11,14 @@ abstract class VideoFileHandlerConfig
|
|||
class VideoFileHandler extends DataHandlerExtension
|
||||
{
|
||||
public const SUPPORTED_MIME = [
|
||||
MIME_TYPE_ASF,
|
||||
MIME_TYPE_AVI,
|
||||
MIME_TYPE_FLASH_VIDEO,
|
||||
MIME_TYPE_MKV,
|
||||
MIME_TYPE_MP4_VIDEO,
|
||||
MIME_TYPE_OGG_VIDEO,
|
||||
MIME_TYPE_QUICKTIME,
|
||||
MIME_TYPE_WEBM,
|
||||
MimeType::ASF,
|
||||
MimeType::AVI,
|
||||
MimeType::FLASH_VIDEO,
|
||||
MimeType::MKV,
|
||||
MimeType::MP4_VIDEO,
|
||||
MimeType::OGG_VIDEO,
|
||||
MimeType::QUICKTIME,
|
||||
MimeType::WEBM,
|
||||
];
|
||||
protected $SUPPORTED_MIME = self::SUPPORTED_MIME;
|
||||
|
||||
|
@ -31,15 +31,15 @@ class VideoFileHandler extends DataHandlerExtension
|
|||
$config->set_default_bool(VideoFileHandlerConfig::PLAYBACK_MUTE, false);
|
||||
$config->set_default_array(
|
||||
VideoFileHandlerConfig::ENABLED_FORMATS,
|
||||
[MIME_TYPE_FLASH_VIDEO, MIME_TYPE_MP4_VIDEO, MIME_TYPE_OGG_VIDEO, MIME_TYPE_WEBM]
|
||||
[MimeType::FLASH_VIDEO, MimeType::MP4_VIDEO, MimeType::OGG_VIDEO, MimeType::WEBM]
|
||||
);
|
||||
}
|
||||
|
||||
private function get_options(): array
|
||||
{
|
||||
$output = [];
|
||||
foreach ($this->SUPPORTED_MIME as $format) {
|
||||
$output[MIME_TYPE_MAP[$format][MIME_TYPE_MAP_NAME]] = $format;
|
||||
foreach ($this->SUPPORTED_MIME as $mime) {
|
||||
$output[MimeMap::get_name_for_mime($mime)] = $mime;
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
@ -108,17 +108,13 @@ class VideoFileHandler extends DataHandlerExtension
|
|||
}
|
||||
}
|
||||
|
||||
protected function supported_ext(string $ext): bool
|
||||
protected function supported_mime(string $mime): bool
|
||||
{
|
||||
global $config;
|
||||
|
||||
$enabled_formats = $config->get_array(VideoFileHandlerConfig::ENABLED_FORMATS);
|
||||
foreach ($enabled_formats as $format) {
|
||||
if (in_array($ext, MIME_TYPE_MAP[$format][MIME_TYPE_MAP_EXT])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
return MimeType::matches_array($mime, $enabled_formats, true);
|
||||
}
|
||||
|
||||
protected function create_thumb(string $hash, string $type): bool
|
||||
|
@ -131,13 +127,11 @@ class VideoFileHandler extends DataHandlerExtension
|
|||
global $config;
|
||||
|
||||
if (file_exists($tmpname)) {
|
||||
$mime = get_mime($tmpname);
|
||||
$mime = MimeType::get_for_file($tmpname);
|
||||
|
||||
$enabled_formats = $config->get_array(VideoFileHandlerConfig::ENABLED_FORMATS);
|
||||
foreach ($enabled_formats as $format) {
|
||||
if (in_array($mime, MIME_TYPE_MAP[$format][MIME_TYPE_MAP_MIME])) {
|
||||
return true;
|
||||
}
|
||||
if (MimeType::matches_array($mime, $enabled_formats)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -7,7 +7,7 @@ class VideoFileHandlerTheme extends Themelet
|
|||
global $config;
|
||||
$ilink = $image->get_image_link();
|
||||
$thumb_url = make_http($image->get_thumb_link()); //used as fallback image
|
||||
$ext = strtolower($image->get_ext());
|
||||
$mime = strtolower($image->get_mime());
|
||||
$full_url = make_http($ilink);
|
||||
$autoplay = $config->get_bool(VideoFileHandlerConfig::PLAYBACK_AUTOPLAY);
|
||||
$loop = $config->get_bool(VideoFileHandlerConfig::PLAYBACK_LOOP);
|
||||
|
@ -26,11 +26,10 @@ class VideoFileHandlerTheme extends Themelet
|
|||
$html = "Video not playing? <a href='$ilink'>Click here</a> to download the file.<br/>";
|
||||
|
||||
//Browser media format support: https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats
|
||||
$mime = get_mime_for_extension($ext);
|
||||
|
||||
if (in_array($mime, VideoFileHandler::SUPPORTED_MIME)) {
|
||||
if (MimeType::matches_array($mime, VideoFileHandler::SUPPORTED_MIME)) {
|
||||
//FLV isn't supported by <video>, but it should always fallback to the flash-based method.
|
||||
if ($mime == MIME_TYPE_WEBM) {
|
||||
if ($mime == MimeType::WEBM) {
|
||||
//Several browsers still lack WebM support sadly: https://caniuse.com/#feat=webm
|
||||
$html .= "<!--[if IE]><p>To view webm files with IE, please <a href='https://tools.google.com/dlpage/webmmf/' target='_blank'>download this plugin</a>.</p><![endif]-->";
|
||||
}
|
||||
|
@ -51,7 +50,7 @@ class VideoFileHandlerTheme extends Themelet
|
|||
<img alt='thumb' src=\"{$thumb_url}\" />
|
||||
</object>";
|
||||
|
||||
if ($mime == MIME_TYPE_FLASH_VIDEO) {
|
||||
if ($mime == MimeType::FLASH_VIDEO) {
|
||||
//FLV doesn't support <video>.
|
||||
$html .= $html_fallback;
|
||||
} else {
|
||||
|
|
|
@ -2,12 +2,14 @@
|
|||
|
||||
abstract class ImageConfig
|
||||
{
|
||||
const VERSION = 'ext_image_version';
|
||||
|
||||
const THUMB_ENGINE = 'thumb_engine';
|
||||
const THUMB_WIDTH = 'thumb_width';
|
||||
const THUMB_HEIGHT = 'thumb_height';
|
||||
const THUMB_SCALING = 'thumb_scaling';
|
||||
const THUMB_QUALITY = 'thumb_quality';
|
||||
const THUMB_TYPE = 'thumb_type';
|
||||
const THUMB_MIME = 'thumb_mime';
|
||||
const THUMB_FIT = 'thumb_fit';
|
||||
|
||||
const SHOW_META = 'image_show_meta';
|
||||
|
@ -17,6 +19,6 @@ abstract class ImageConfig
|
|||
const EXPIRES = 'image_expires';
|
||||
const UPLOAD_COLLISION_HANDLER = 'upload_collision_handler';
|
||||
|
||||
const COLLISION_MERGE = 'merge';
|
||||
const COLLISION_ERROR = 'error';
|
||||
const COLLISION_MERGE = 'merge';
|
||||
const COLLISION_ERROR = 'error';
|
||||
}
|
||||
|
|
|
@ -10,19 +10,21 @@ class ImageIO extends Extension
|
|||
/** @var ImageIOTheme */
|
||||
protected $theme;
|
||||
|
||||
const COLLISION_OPTIONS = ['Error'=>ImageConfig::COLLISION_ERROR, 'Merge'=>ImageConfig::COLLISION_MERGE];
|
||||
const COLLISION_OPTIONS = [
|
||||
'Error'=>ImageConfig::COLLISION_ERROR,
|
||||
'Merge'=>ImageConfig::COLLISION_MERGE
|
||||
];
|
||||
|
||||
const EXIF_READ_FUNCTION = "exif_read_data";
|
||||
|
||||
|
||||
const THUMBNAIL_ENGINES = [
|
||||
'Built-in GD' => MediaEngine::GD,
|
||||
'ImageMagick' => MediaEngine::IMAGICK
|
||||
];
|
||||
|
||||
const THUMBNAIL_TYPES = [
|
||||
'JPEG' => EXTENSION_JPG,
|
||||
'WEBP (Not IE/Safari compatible)' => EXTENSION_WEBP
|
||||
'JPEG' => MimeType::JPEG,
|
||||
'WEBP (Not IE/Safari compatible)' => MimeType::WEBP
|
||||
];
|
||||
|
||||
public function onInitExt(InitExtEvent $event)
|
||||
|
@ -33,7 +35,7 @@ class ImageIO extends Extension
|
|||
$config->set_default_int(ImageConfig::THUMB_HEIGHT, 192);
|
||||
$config->set_default_int(ImageConfig::THUMB_SCALING, 100);
|
||||
$config->set_default_int(ImageConfig::THUMB_QUALITY, 75);
|
||||
$config->set_default_string(ImageConfig::THUMB_TYPE, EXTENSION_JPG);
|
||||
$config->set_default_string(ImageConfig::THUMB_MIME, MimeType::JPEG);
|
||||
$config->set_default_string(ImageConfig::THUMB_FIT, Media::RESIZE_TYPE_FIT);
|
||||
|
||||
if (function_exists(self::EXIF_READ_FUNCTION)) {
|
||||
|
@ -46,6 +48,25 @@ class ImageIO extends Extension
|
|||
$config->set_default_int(ImageConfig::EXPIRES, (60*60*24*31)); // defaults to one month
|
||||
}
|
||||
|
||||
public function onDatabaseUpgrade(DatabaseUpgradeEvent $event)
|
||||
{
|
||||
global $config;
|
||||
|
||||
if ($this->get_version(ImageConfig::VERSION) < 1) {
|
||||
switch ($config->get_string("thumb_type")) {
|
||||
case FileExtension::WEBP:
|
||||
$config->set_string(ImageConfig::THUMB_MIME, MimeType::WEBP);
|
||||
break;
|
||||
case FileExtension::JPEG:
|
||||
$config->set_string(ImageConfig::THUMB_MIME, MimeType::JPEG);
|
||||
break;
|
||||
}
|
||||
$config->set_string("thumb_type", null);
|
||||
|
||||
$this->set_version(ImageConfig::VERSION, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public function onPageRequest(PageRequestEvent $event)
|
||||
{
|
||||
if ($event->page_matches("image/delete")) {
|
||||
|
@ -164,6 +185,10 @@ class ImageIO extends Extension
|
|||
$id = $event->id;
|
||||
$image = $event->image;
|
||||
|
||||
$image->set_mime(
|
||||
MimeType::get_for_file($image->get_image_filename())
|
||||
);
|
||||
|
||||
/* Check to make sure the image exists. */
|
||||
$existing = Image::by_id($id);
|
||||
|
||||
|
@ -197,7 +222,7 @@ class ImageIO extends Extension
|
|||
$existing->remove_image_only(); // Actually delete the old image file from disk
|
||||
|
||||
/* Generate new thumbnail */
|
||||
send_event(new ThumbnailGenerationEvent($image->hash, strtolower($image->ext)));
|
||||
send_event(new ThumbnailGenerationEvent($image->hash, strtolower($image->get_mime())));
|
||||
|
||||
log_info("image", "Replaced Image #{$id} with ({$image->hash})");
|
||||
} catch (ImageReplaceException $e) {
|
||||
|
@ -236,7 +261,7 @@ class ImageIO extends Extension
|
|||
$sb = new SetupBlock("Thumbnailing");
|
||||
$sb->start_table();
|
||||
$sb->add_choice_option(ImageConfig::THUMB_ENGINE, self::THUMBNAIL_ENGINES, "Engine", true);
|
||||
$sb->add_choice_option(ImageConfig::THUMB_TYPE, self::THUMBNAIL_TYPES, "Filetype", true);
|
||||
$sb->add_choice_option(ImageConfig::THUMB_MIME, self::THUMBNAIL_TYPES, "Filetype", true);
|
||||
|
||||
$sb->add_int_option(ImageConfig::THUMB_WIDTH, "Max Width", true);
|
||||
$sb->add_int_option(ImageConfig::THUMB_HEIGHT, "Max Height", true);
|
||||
|
@ -277,12 +302,10 @@ class ImageIO extends Extension
|
|||
$image = Image::by_id($image_id);
|
||||
if (!is_null($image)) {
|
||||
if ($type == "thumb") {
|
||||
$ext = $config->get_string(ImageConfig::THUMB_TYPE);
|
||||
$page->set_type(get_mime_for_extension($ext));
|
||||
|
||||
$mime = $config->get_string(ImageConfig::THUMB_MIME);
|
||||
$file = $image->get_thumb_filename();
|
||||
} else {
|
||||
$page->set_type($image->get_mime_type());
|
||||
$mime = $image->get_mime();
|
||||
$file = $image->get_image_filename();
|
||||
}
|
||||
if (!file_exists($file)) {
|
||||
|
@ -290,6 +313,8 @@ class ImageIO extends Extension
|
|||
die();
|
||||
}
|
||||
|
||||
$page->set_mime($mime);
|
||||
|
||||
|
||||
if (isset($_SERVER["HTTP_IF_MODIFIED_SINCE"])) {
|
||||
$if_modified_since = preg_replace('/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"]);
|
||||
|
@ -319,7 +344,7 @@ class ImageIO extends Extension
|
|||
$page->add_http_header('Expires: ' . $expires);
|
||||
}
|
||||
|
||||
send_event(new ImageDownloadingEvent($image, $file, $image->get_mime_type()));
|
||||
send_event(new ImageDownloadingEvent($image, $file, $image->get_mime()));
|
||||
} else {
|
||||
$page->set_title("Not Found");
|
||||
$page->set_heading("Not Found");
|
||||
|
|
|
@ -20,22 +20,26 @@ Shimmie extensions may provide other filters:
|
|||
<li>pie
|
||||
<li>somethi* -- wildcards are supported
|
||||
</ul>
|
||||
</li>
|
||||
<li>size (=, <, >, <=, >=) width x height, eg
|
||||
<ul>
|
||||
<li>size=1024x768 -- a specific wallpaper size
|
||||
<li>size>=500x500 -- no small images
|
||||
<li>size<1000x1000 -- no large images
|
||||
</ul>
|
||||
</li>
|
||||
<li>width (=, <, >, <=, >=) width, eg
|
||||
<ul>
|
||||
<li>width=1024 -- find images with 1024 width
|
||||
<li>width>2000 -- find images bigger than 2000 width
|
||||
</ul>
|
||||
</li>
|
||||
<li>height (=, <, >, <=, >=) height, eg
|
||||
<ul>
|
||||
<li>height=768 -- find images with 768 height
|
||||
<li>height>1000 -- find images bigger than 1000 height
|
||||
</ul>
|
||||
</li>
|
||||
<li>ratio (=, <, >, <=, >=) width : height, eg
|
||||
<ul>
|
||||
<li>ratio=4:3, ratio=16:9 -- standard wallpaper
|
||||
|
@ -43,66 +47,73 @@ Shimmie extensions may provide other filters:
|
|||
<li>ratio<1:1 -- tall images
|
||||
<li>ratio>1:1 -- wide images
|
||||
</ul>
|
||||
</li>
|
||||
<li>filesize (=, <, >, <=, >=) size, eg
|
||||
<ul>
|
||||
<li>filesize>1024 -- no images under 1KB
|
||||
<li>filesize<=3MB -- shorthand filesizes are supported too
|
||||
</ul>
|
||||
</li>
|
||||
<li>id (=, <, >, <=, >=) number, eg
|
||||
<ul>
|
||||
<li>id<20 -- search only the first few images
|
||||
<li>id>=500 -- search later images
|
||||
</ul>
|
||||
</li>
|
||||
<li>user=Username & poster=Username, eg
|
||||
<ul>
|
||||
<li>user=Shish -- find all of Shish's posts
|
||||
<li>poster=Shish -- same as above
|
||||
</ul>
|
||||
</li>
|
||||
<li>user_id=userID & poster_id=userID, eg
|
||||
<ul>
|
||||
<li>user_id=2 -- find all posts by user id 2
|
||||
<li>poster_id=2 -- same as above
|
||||
</ul>
|
||||
</li>
|
||||
<li>hash=md5sum & md5=md5sum, eg
|
||||
<ul>
|
||||
<li>hash=bf5b59173f16b6937a4021713dbfaa72 -- find the \"Taiga want up!\" image
|
||||
<li>md5=bf5b59173f16b6937a4021713dbfaa72 -- same as above
|
||||
</ul>
|
||||
<li>filetype=type & ext=type, eg
|
||||
<ul>
|
||||
<li>filetype=png -- find all PNG images
|
||||
<li>ext=png -- same as above
|
||||
</ul>
|
||||
</li>
|
||||
<li>filename=blah & name=blah, eg
|
||||
<ul>
|
||||
<li>filename=kitten -- find all images with \"kitten\" in the original filename
|
||||
<li>name=kitten -- same as above
|
||||
</ul>
|
||||
</li>
|
||||
<li>posted (=, <, >, <=, >=) date, eg
|
||||
<ul>
|
||||
<li>posted>=2009-12-25 posted<=2010-01-01 -- find images posted between christmas and new year
|
||||
</ul>
|
||||
</li>
|
||||
<li>tags (=, <, >, <=, >=) count, eg
|
||||
<ul>
|
||||
<li>tags=1 -- search for images with only 1 tag
|
||||
<li>tags>=10 -- search for images with 10 or more tags
|
||||
<li>tags<25 -- search for images with less than 25 tags
|
||||
</ul>
|
||||
</li>
|
||||
<li>source=(URL, any, none) eg
|
||||
<ul>
|
||||
<li>source=http://example.com -- find all images with \"http://example.com\" in the source
|
||||
<li>source=any -- find all images with a source
|
||||
<li>source=none -- find all images without a source
|
||||
</ul>
|
||||
</li>
|
||||
<li>order=(id, width, height, filesize, filename)_(ASC, DESC), eg
|
||||
<ul>
|
||||
<li>order=width -- find all images sorted from highest > lowest width
|
||||
<li>order=filesize_asc -- find all images sorted from lowest > highest filesize
|
||||
</ul>
|
||||
</li>
|
||||
<li>order=random_####, eg
|
||||
<ul>
|
||||
<li>order=random_8547 -- find all images sorted randomly using 8547 as a seed
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Search items can be combined to search for images which match both,
|
||||
or you can stick \"-\" in front of an item to search for things that don't
|
||||
|
|
|
@ -208,9 +208,6 @@ class Index extends Extension
|
|||
} elseif (preg_match("/^(phash)[=|:]([0-9a-fA-F]*)$/i", $event->term, $matches)) {
|
||||
$phash = strtolower($matches[2]);
|
||||
$event->add_querylet(new Querylet('images.phash = :phash', ["phash" => $phash]));
|
||||
} elseif (preg_match("/^(filetype|ext)[=|:]([a-zA-Z0-9]*)$/i", $event->term, $matches)) {
|
||||
$ext = strtolower($matches[2]);
|
||||
$event->add_querylet(new Querylet('images.ext = :ext', ["ext" => $ext]));
|
||||
} elseif (preg_match("/^(filename|name)[=|:]([a-zA-Z0-9]*)$/i", $event->term, $matches)) {
|
||||
$filename = strtolower($matches[2]);
|
||||
$event->add_querylet(new Querylet("images.filename LIKE :filename{$this->stpen}", ["filename{$this->stpen}"=>"%$filename%"]));
|
||||
|
|
|
@ -187,44 +187,44 @@ and of course start organising your images :-)
|
|||
<pre>tagname</pre>
|
||||
<p>Returns images that are tagged with "tagname".</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="command_example">
|
||||
<pre>tagname othertagname</pre>
|
||||
<p>Returns images that are tagged with "tagname" and "othertagname".</p>
|
||||
<p>Returns images that are tagged with "tagname" and "othertagname".</p>
|
||||
</div>
|
||||
|
||||
<p>Most tags and keywords can be prefaced with a negative sign (-) to indicate that you want to search for images that do not match something.</p>
|
||||
|
||||
<div class="command_example">
|
||||
<pre>-tagname</pre>
|
||||
<p>Returns images that are not tagged with "tagname".</p>
|
||||
<p>Returns images that are not tagged with "tagname".</p>
|
||||
</div>
|
||||
|
||||
<div class="command_example">
|
||||
<pre>-tagname -othertagname</pre>
|
||||
<p>Returns images that are not tagged with "tagname" and "othertagname". This is different than without the negative sign, as images with "tagname" or "othertagname" can still be returned as long as the other one is not present.</p>
|
||||
<p>Returns images that are not tagged with "tagname" and "othertagname". This is different than without the negative sign, as images with "tagname" or "othertagname" can still be returned as long as the other one is not present.</p>
|
||||
</div>
|
||||
|
||||
<div class="command_example">
|
||||
<pre>tagname -othertagname</pre>
|
||||
<p>Returns images that are tagged with "tagname", but are not tagged with "othertagname".</p>
|
||||
<p>Returns images that are tagged with "tagname", but are not tagged with "othertagname".</p>
|
||||
</div>
|
||||
|
||||
<p>Wildcard searches are possible as well using * for "any one, more, or none" and ? for "any one".</p>
|
||||
|
||||
<div class="command_example">
|
||||
<pre>tagn*</pre>
|
||||
<p>Returns images that are tagged with "tagname", "tagnot", or anything else that starts with "tagn".</p>
|
||||
<p>Returns images that are tagged with "tagname", "tagnot", or anything else that starts with "tagn".</p>
|
||||
</div>
|
||||
|
||||
<div class="command_example">
|
||||
<pre>tagn?me</pre>
|
||||
<p>Returns images that are tagged with "tagname", "tagnome", or anything else that starts with "tagn", has one character, and ends with "me".</p>
|
||||
<p>Returns images that are tagged with "tagname", "tagnome", or anything else that starts with "tagn", has one character, and ends with "me".</p>
|
||||
</div>
|
||||
|
||||
<div class="command_example">
|
||||
<pre>tags=1</pre>
|
||||
<p>Returns images with exactly 1 tag.</p>
|
||||
<p>Returns images with exactly 1 tag.</p>
|
||||
</div>
|
||||
|
||||
<div class="command_example">
|
||||
|
@ -235,12 +235,12 @@ and of course start organising your images :-)
|
|||
<p>Can use <, <=, >, >=, or =.</p>
|
||||
|
||||
<hr/>
|
||||
|
||||
|
||||
<p>Search for images by aspect ratio</p>
|
||||
|
||||
|
||||
<div class="command_example">
|
||||
<pre>ratio=4:3</pre>
|
||||
<p>Returns images with an aspect ratio of 4:3.</p>
|
||||
<p>Returns images with an aspect ratio of 4:3.</p>
|
||||
</div>
|
||||
|
||||
<div class="command_example">
|
||||
|
@ -249,14 +249,14 @@ and of course start organising your images :-)
|
|||
</div>
|
||||
|
||||
<p>Can use <, <=, >, >=, or =. The relation is calculated by dividing width by height.</p>
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
<p>Search for images by file size</p>
|
||||
|
||||
|
||||
<div class="command_example">
|
||||
<pre>filesize=1</pre>
|
||||
<p>Returns images exactly 1 byte in size.</p>
|
||||
<p>Returns images exactly 1 byte in size.</p>
|
||||
</div>
|
||||
|
||||
<div class="command_example">
|
||||
|
@ -265,71 +265,62 @@ and of course start organising your images :-)
|
|||
</div>
|
||||
|
||||
<p>Can use <, <=, >, >=, or =. Supported suffixes are kb, mb, and gb. Uses multiples of 1024.</p>
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
<p>Search for images by MD5 hash</p>
|
||||
|
||||
|
||||
<div class="command_example">
|
||||
<pre>hash=0D3512CAA964B2BA5D7851AF5951F33B</pre>
|
||||
<p>Returns image with an MD5 hash 0D3512CAA964B2BA5D7851AF5951F33B.</p>
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
<p>Search for images by file type</p>
|
||||
|
||||
<div class="command_example">
|
||||
<pre>filetype=jpg</pre>
|
||||
<p>Returns images that are of type "jpg".</p>
|
||||
<p>Returns image with an MD5 hash 0D3512CAA964B2BA5D7851AF5951F33B.</p>
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
<p>Search for images by file name</p>
|
||||
|
||||
|
||||
<div class="command_example">
|
||||
<pre>filename=picasso.jpg</pre>
|
||||
<p>Returns images that are named "picasso.jpg".</p>
|
||||
<p>Returns images that are named "picasso.jpg".</p>
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
<p>Search for images by source</p>
|
||||
|
||||
|
||||
<div class="command_example">
|
||||
<pre>source=http://google.com/</pre>
|
||||
<p>Returns images with a source of "http://google.com/".</p>
|
||||
<p>Returns images with a source of "http://google.com/".</p>
|
||||
</div>
|
||||
|
||||
<div class="command_example">
|
||||
<pre>source=any</pre>
|
||||
<p>Returns images with a source set.</p>
|
||||
<p>Returns images with a source set.</p>
|
||||
</div>
|
||||
|
||||
<div class="command_example">
|
||||
<pre>source=none</pre>
|
||||
<p>Returns images without a source set.</p>
|
||||
<p>Returns images without a source set.</p>
|
||||
</div>
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
<p>Search for images by date posted.</p>
|
||||
|
||||
<div class="command_example">
|
||||
<pre>posted>=07-19-2019</pre>
|
||||
<p>Returns images posted on or after 07-19-2019.</p>
|
||||
<p>Returns images posted on or after 07-19-2019.</p>
|
||||
</div>
|
||||
|
||||
<p>Can use <, <=, >, >=, or =. Date format is mm-dd-yyyy. Date posted includes time component, so = will not work unless the time is exact.</p>
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
<p>Search for images by image dimensions</p>
|
||||
|
||||
<div class="command_example">
|
||||
<pre>size=640x480</pre>
|
||||
<p>Returns images exactly 640 pixels wide by 480 pixels high.</p>
|
||||
<p>Returns images exactly 640 pixels wide by 480 pixels high.</p>
|
||||
</div>
|
||||
|
||||
<div class="command_example">
|
||||
|
@ -348,21 +339,21 @@ and of course start organising your images :-)
|
|||
</div>
|
||||
|
||||
<p>Can use <, <=, >, >=, or =.</p>
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
|
||||
<p>Sorting search results can be done using the pattern order:field_direction. _direction can be either _asc or _desc, indicating ascending (123) or descending (321) order.</p>
|
||||
|
||||
|
||||
<div class="command_example">
|
||||
<pre>order:id_asc</pre>
|
||||
<p>Returns images sorted by ID, smallest first.</p>
|
||||
<p>Returns images sorted by ID, smallest first.</p>
|
||||
</div>
|
||||
|
||||
<div class="command_example">
|
||||
<pre>order:width_desc</pre>
|
||||
<p>Returns images sorted by width, largest first.</p>
|
||||
</div>
|
||||
|
||||
|
||||
<p>These fields are supported:
|
||||
<ul>
|
||||
<li>id</li>
|
||||
|
|
|
@ -4,9 +4,9 @@ class MediaResizeEvent extends Event
|
|||
{
|
||||
public $engine;
|
||||
public $input_path;
|
||||
public $input_type;
|
||||
public $input_mime;
|
||||
public $output_path;
|
||||
public $target_format;
|
||||
public $target_mime;
|
||||
public $target_width;
|
||||
public $target_height;
|
||||
public $target_quality;
|
||||
|
@ -17,12 +17,12 @@ class MediaResizeEvent extends Event
|
|||
public function __construct(
|
||||
String $engine,
|
||||
string $input_path,
|
||||
string $input_type,
|
||||
string $input_mime,
|
||||
string $output_path,
|
||||
int $target_width,
|
||||
int $target_height,
|
||||
string $resize_type = Media::RESIZE_TYPE_FIT,
|
||||
string $target_format = null,
|
||||
string $target_mime = null,
|
||||
int $target_quality = 80,
|
||||
bool $minimize = false,
|
||||
bool $allow_upscale = true
|
||||
|
@ -31,11 +31,11 @@ class MediaResizeEvent extends Event
|
|||
assert(in_array($engine, MediaEngine::ALL));
|
||||
$this->engine = $engine;
|
||||
$this->input_path = $input_path;
|
||||
$this->input_type = $input_type;
|
||||
$this->input_mime = $input_mime;
|
||||
$this->output_path = $output_path;
|
||||
$this->target_height = $target_height;
|
||||
$this->target_width = $target_width;
|
||||
$this->target_format = $target_format;
|
||||
$this->target_mime = $target_mime;
|
||||
$this->target_quality = $target_quality;
|
||||
$this->minimize = $minimize;
|
||||
$this->allow_upscale = $allow_upscale;
|
||||
|
@ -47,13 +47,13 @@ class MediaCheckPropertiesEvent extends Event
|
|||
{
|
||||
public $image;
|
||||
public $file_name;
|
||||
public $ext;
|
||||
public $mime;
|
||||
|
||||
public function __construct(Image $image)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->image = $image;
|
||||
$this->file_name = warehouse_path(Image::IMAGE_DIR, $image->hash);
|
||||
$this->ext = strtolower($image->ext);
|
||||
$this->mime = strtolower($image->get_mime());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,4 +11,5 @@ class MediaInfo extends ExtensionInfo
|
|||
public $license = self::LICENSE_WTFPL;
|
||||
public $description = "Provides common functions and settings used for media operations.";
|
||||
public $core = true;
|
||||
public $visibility = self::VISIBLE_HIDDEN;
|
||||
}
|
||||
|
|
|
@ -16,36 +16,25 @@ class Media extends Extension
|
|||
/** @var MediaTheme */
|
||||
protected $theme;
|
||||
|
||||
const WEBP_LOSSY = "webp-lossy";
|
||||
const WEBP_LOSSLESS = "webp-lossless";
|
||||
|
||||
const IMAGE_MEDIA_ENGINES = [
|
||||
"GD" => MediaEngine::GD,
|
||||
"ImageMagick" => MediaEngine::IMAGICK,
|
||||
];
|
||||
|
||||
const LOSSLESS_FORMATS = [
|
||||
self::WEBP_LOSSLESS,
|
||||
EXTENSION_PNG,
|
||||
EXTENSION_PSD,
|
||||
EXTENSION_BMP,
|
||||
EXTENSION_ICO,
|
||||
EXTENSION_CUR,
|
||||
EXTENSION_ANI,
|
||||
EXTENSION_GIF
|
||||
|
||||
MimeType::WEBP_LOSSLESS,
|
||||
MimeType::PNG,
|
||||
MimeType::PSD,
|
||||
MimeType::BMP,
|
||||
MimeType::ICO,
|
||||
MimeType::ANI,
|
||||
MimeType::GIF
|
||||
];
|
||||
|
||||
const ALPHA_FORMATS = [
|
||||
self::WEBP_LOSSLESS,
|
||||
self::WEBP_LOSSY,
|
||||
EXTENSION_WEBP,
|
||||
EXTENSION_PNG,
|
||||
];
|
||||
|
||||
const FORMAT_ALIASES = [
|
||||
EXTENSION_TIF => EXTENSION_TIFF,
|
||||
EXTENSION_JPEG => EXTENSION_JPG,
|
||||
MimeType::WEBP_LOSSLESS,
|
||||
MimeType::WEBP,
|
||||
MimeType::PNG,
|
||||
];
|
||||
|
||||
const RESIZE_TYPE_FIT = "Fit";
|
||||
|
@ -53,16 +42,6 @@ class Media extends Extension
|
|||
const RESIZE_TYPE_FILL = "Fill";
|
||||
const RESIZE_TYPE_STRETCH = "Stretch";
|
||||
|
||||
//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];
|
||||
|
||||
|
||||
public static function imagick_available(): bool
|
||||
{
|
||||
return extension_loaded("imagick");
|
||||
|
@ -128,22 +107,6 @@ class Media extends Extension
|
|||
$event->panel->add_block($sb);
|
||||
}
|
||||
|
||||
public function onAdminBuilding(AdminBuildingEvent $event)
|
||||
{
|
||||
global $database;
|
||||
$types = $database->get_all("SELECT ext, count(*) count FROM images group by ext");
|
||||
|
||||
$this->theme->display_form($types);
|
||||
}
|
||||
|
||||
public function onAdminAction(AdminActionEvent $event)
|
||||
{
|
||||
$action = $event->action;
|
||||
if (method_exists($this, $action)) {
|
||||
$event->redirect = $this->$action();
|
||||
}
|
||||
}
|
||||
|
||||
public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event)
|
||||
{
|
||||
global $user;
|
||||
|
@ -210,7 +173,10 @@ class Media extends Extension
|
|||
*/
|
||||
public function onMediaResize(MediaResizeEvent $event)
|
||||
{
|
||||
if (!in_array($event->resize_type, MediaEngine::RESIZE_TYPE_SUPPORT[MediaEngine::IMAGICK])) {
|
||||
if (!in_array(
|
||||
$event->resize_type,
|
||||
MediaEngine::RESIZE_TYPE_SUPPORT[$event->engine]
|
||||
)) {
|
||||
throw new MediaException("Resize type $event->resize_type not supported by selected media engine $event->engine");
|
||||
}
|
||||
|
||||
|
@ -227,7 +193,7 @@ class Media extends Extension
|
|||
$event->target_width,
|
||||
$event->target_height,
|
||||
$event->output_path,
|
||||
$event->target_format,
|
||||
$event->target_mime,
|
||||
$event->resize_type,
|
||||
$event->target_quality,
|
||||
$event->allow_upscale
|
||||
|
@ -239,11 +205,11 @@ class Media extends Extension
|
|||
// } else {
|
||||
self::image_resize_convert(
|
||||
$event->input_path,
|
||||
$event->input_type,
|
||||
$event->input_mime,
|
||||
$event->target_width,
|
||||
$event->target_height,
|
||||
$event->output_path,
|
||||
$event->target_format,
|
||||
$event->target_mime,
|
||||
$event->resize_type,
|
||||
$event->target_quality,
|
||||
$event->minimize,
|
||||
|
@ -315,8 +281,6 @@ class Media extends Extension
|
|||
$s = ((int)($event->image->length / 100))/10;
|
||||
$event->replace('$size', "${s}s");
|
||||
}
|
||||
|
||||
$event->replace('$ext', $event->image->ext);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -372,7 +336,7 @@ class Media extends Extension
|
|||
|
||||
$codec = "mjpeg";
|
||||
$quality = $config->get_int(ImageConfig::THUMB_QUALITY);
|
||||
if ($config->get_string(ImageConfig::THUMB_TYPE) == EXTENSION_WEBP) {
|
||||
if ($config->get_string(ImageConfig::THUMB_MIME) == MimeType::WEBP) {
|
||||
$codec = "libwebp";
|
||||
} else {
|
||||
// mjpeg quality ranges from 2-31, with 2 being the best quality.
|
||||
|
@ -399,7 +363,7 @@ class Media extends Extension
|
|||
if ((int)$ret == (int)0) {
|
||||
log_debug('media', "Generating thumbnail with command `$cmd`, returns $ret");
|
||||
|
||||
create_scaled_image($tmpname, $outname, $scaled_size, "png");
|
||||
create_scaled_image($tmpname, $outname, $scaled_size, MimeType::PNG);
|
||||
|
||||
|
||||
return true;
|
||||
|
@ -442,22 +406,19 @@ class Media extends Extension
|
|||
}
|
||||
}
|
||||
|
||||
public static function determine_ext(string $format): string
|
||||
public static function determine_ext(string $mime): string
|
||||
{
|
||||
$format = self::normalize_format($format);
|
||||
switch ($format) {
|
||||
case self::WEBP_LOSSLESS:
|
||||
case self::WEBP_LOSSY:
|
||||
return EXTENSION_WEBP;
|
||||
default:
|
||||
return $format;
|
||||
$ext = FileExtension::get_for_mime($mime);
|
||||
if (empty($ext)) {
|
||||
throw new SCoreException("Could not determine extension for $mime");
|
||||
}
|
||||
return $ext;
|
||||
}
|
||||
|
||||
// private static function image_save_imagick(Imagick $image, string $path, string $format, int $output_quality = 80, bool $minimize)
|
||||
// {
|
||||
// switch ($format) {
|
||||
// case EXTENSION_PNG:
|
||||
// case FileExtension::PNG:
|
||||
// $result = $image->setOption('png:compression-level', 9);
|
||||
// if ($result !== true) {
|
||||
// throw new GraphicsException("Could not set png compression option");
|
||||
|
@ -561,14 +522,14 @@ class Media extends Extension
|
|||
// }
|
||||
// }
|
||||
|
||||
public static function is_lossless(string $filename, string $format)
|
||||
public static function is_lossless(string $filename, string $mime)
|
||||
{
|
||||
if (in_array($format, self::LOSSLESS_FORMATS)) {
|
||||
if (in_array($mime, self::LOSSLESS_FORMATS)) {
|
||||
return true;
|
||||
}
|
||||
switch ($format) {
|
||||
case EXTENSION_WEBP:
|
||||
return self::is_lossless_webp($filename);
|
||||
switch ($mime) {
|
||||
case MimeType::WEBP:
|
||||
return MimeType::is_lossless_webp($filename);
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
|
@ -576,11 +537,11 @@ class Media extends Extension
|
|||
|
||||
public static function image_resize_convert(
|
||||
string $input_path,
|
||||
string $input_type,
|
||||
string $input_mime,
|
||||
int $new_width,
|
||||
int $new_height,
|
||||
string $output_filename,
|
||||
string $output_type = null,
|
||||
string $output_mime = null,
|
||||
string $resize_type = self::RESIZE_TYPE_FIT,
|
||||
int $output_quality = 80,
|
||||
bool $minimize = false,
|
||||
|
@ -594,21 +555,18 @@ class Media extends Extension
|
|||
throw new MediaException("convert command not configured");
|
||||
}
|
||||
|
||||
if (empty($output_type)) {
|
||||
$output_type = $input_type;
|
||||
if (empty($output_mime)) {
|
||||
$output_mime = $input_mime;
|
||||
}
|
||||
|
||||
if ($output_type==EXTENSION_WEBP && self::is_lossless($input_path, $input_type)) {
|
||||
$output_type = self::WEBP_LOSSLESS;
|
||||
if ($output_mime==MimeType::WEBP && self::is_lossless($input_path, $input_mime)) {
|
||||
$output_mime = MimeType::WEBP_LOSSLESS;
|
||||
}
|
||||
|
||||
$bg = "black";
|
||||
if (self::supports_alpha($output_type)) {
|
||||
if (self::supports_alpha($output_mime)) {
|
||||
$bg = "none";
|
||||
}
|
||||
if (!empty($input_type)) {
|
||||
$input_type = $input_type . ":";
|
||||
}
|
||||
|
||||
$resize_suffix = "";
|
||||
if (!$allow_upscale) {
|
||||
|
@ -625,7 +583,9 @@ class Media extends Extension
|
|||
$resize_arg = "-thumbnail";
|
||||
}
|
||||
|
||||
$file_arg = "${input_type}\"${input_path}[0]\"";
|
||||
$input_ext = self::determine_ext($input_mime);
|
||||
|
||||
$file_arg = "${input_ext}:\"${input_path}[0]\"";
|
||||
|
||||
switch ($resize_type) {
|
||||
case Media::RESIZE_TYPE_FIT:
|
||||
|
@ -633,24 +593,24 @@ class Media extends Extension
|
|||
$args .= "${resize_arg} ${new_width}x${new_height}${resize_suffix} ${file_arg}";
|
||||
break;
|
||||
case Media::RESIZE_TYPE_FILL:
|
||||
$args .= "${resize_arg} ${new_width}x${new_height}\^ -gravity center -extent ${new_width}x${new_height} ${file_arg}";
|
||||
$args .= "${resize_arg} ${new_width}x${new_height}\^ -background none -gravity center -extent ${new_width}x${new_height} ${file_arg}";
|
||||
break;
|
||||
case Media::RESIZE_TYPE_FIT_BLUR:
|
||||
$blur_size = max(ceil(max($new_width, $new_height) / 25), 5);
|
||||
$args .= "${file_arg} ".
|
||||
"\( -clone 0 -resize ${new_width}x${new_height}\^ -gravity center -fill black -colorize 50% -extent ${new_width}x${new_height} -blur 0x${blur_size} \) ".
|
||||
"\( -clone 0 -resize ${new_width}x${new_height}\^ -background none -gravity center -fill black -colorize 50% -extent ${new_width}x${new_height} -blur 0x${blur_size} \) ".
|
||||
"\( -clone 0 -resize ${new_width}x${new_height} \) ".
|
||||
"-delete 0 -gravity center -compose over -composite";
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
switch ($output_type) {
|
||||
case Media::WEBP_LOSSLESS:
|
||||
$args .= '-define webp:lossless=true';
|
||||
switch ($output_mime) {
|
||||
case MimeType::WEBP_LOSSLESS:
|
||||
$args .= ' -define webp:lossless=true';
|
||||
break;
|
||||
case EXTENSION_PNG:
|
||||
$args .= '-define png:compression-level=9';
|
||||
case MimeType::PNG:
|
||||
$args .= ' -define png:compression-level=9';
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -658,7 +618,7 @@ class Media extends Extension
|
|||
$args .= " -quality ${output_quality} -background ${bg}";
|
||||
|
||||
|
||||
$output_ext = self::determine_ext($output_type);
|
||||
$output_ext = self::determine_ext($output_mime);
|
||||
|
||||
$format = '"%s" %s %s:"%s" 2>&1';
|
||||
$cmd = sprintf($format, $convert, $args, $output_ext, $output_filename);
|
||||
|
@ -679,7 +639,7 @@ class Media extends Extension
|
|||
* @param int $new_width
|
||||
* @param int $new_height
|
||||
* @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_mime 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.
|
||||
* @throws MediaException
|
||||
* @throws InsufficientMemoryException if the estimated memory usage exceeds the memory limit.
|
||||
|
@ -690,7 +650,7 @@ class Media extends Extension
|
|||
int $new_width,
|
||||
int $new_height,
|
||||
string $output_filename,
|
||||
string $output_type = null,
|
||||
string $output_mime = null,
|
||||
string $resize_type = self::RESIZE_TYPE_FIT,
|
||||
int $output_quality = 80,
|
||||
bool $allow_upscale = true
|
||||
|
@ -698,26 +658,26 @@ class Media extends Extension
|
|||
$width = $info[0];
|
||||
$height = $info[1];
|
||||
|
||||
if ($output_type == null) {
|
||||
if ($output_mime == null) {
|
||||
/* If not specified, output to the same format as the original image */
|
||||
switch ($info[2]) {
|
||||
case IMAGETYPE_GIF:
|
||||
$output_type = EXTENSION_GIF;
|
||||
$output_mime = MimeType::GIF;
|
||||
break;
|
||||
case IMAGETYPE_JPEG:
|
||||
$output_type = EXTENSION_JPEG;
|
||||
$output_mime = MimeType::JPEG;
|
||||
break;
|
||||
case IMAGETYPE_PNG:
|
||||
$output_type = EXTENSION_PNG;
|
||||
$output_mime = MimeType::PNG;
|
||||
break;
|
||||
case IMAGETYPE_WEBP:
|
||||
$output_type = EXTENSION_WEBP;
|
||||
$output_mime = MimeType::WEBP;
|
||||
break;
|
||||
case IMAGETYPE_BMP:
|
||||
$output_type = EXTENSION_BMP;
|
||||
$output_mime = MimeType::BMP;
|
||||
break;
|
||||
default:
|
||||
throw new MediaException("Failed to save the new image - Unsupported image type.");
|
||||
throw new MediaException("Failed to save the new image - Unsupported MIME type.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -809,29 +769,27 @@ class Media extends Extension
|
|||
throw new MediaException("Unable to copy resized image data to new image");
|
||||
}
|
||||
|
||||
switch ($output_type) {
|
||||
case EXTENSION_BMP:
|
||||
switch ($output_mime) {
|
||||
case MimeType::BMP:
|
||||
$result = imagebmp($image_resized, $output_filename, true);
|
||||
break;
|
||||
case EXTENSION_WEBP:
|
||||
case Media::WEBP_LOSSY:
|
||||
case MimeType::WEBP:
|
||||
$result = imagewebp($image_resized, $output_filename, $output_quality);
|
||||
break;
|
||||
case EXTENSION_JPG:
|
||||
case EXTENSION_JPEG:
|
||||
case MimeType::JPEG:
|
||||
$result = imagejpeg($image_resized, $output_filename, $output_quality);
|
||||
break;
|
||||
case EXTENSION_PNG:
|
||||
case MimeType::PNG:
|
||||
$result = imagepng($image_resized, $output_filename, 9);
|
||||
break;
|
||||
case EXTENSION_GIF:
|
||||
case MimeType::GIF:
|
||||
$result = imagegif($image_resized, $output_filename);
|
||||
break;
|
||||
default:
|
||||
throw new MediaException("Failed to save the new image - Unsupported image type: $output_type");
|
||||
throw new MediaException("Failed to save the new image - Unsupported image type: $output_mime");
|
||||
}
|
||||
if ($result === false) {
|
||||
throw new MediaException("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_mime");
|
||||
}
|
||||
} finally {
|
||||
@imagedestroy($image);
|
||||
|
@ -839,117 +797,10 @@ class Media extends Extension
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a file is an animated gif.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
public static function is_animated_gif(string $image_filename): bool
|
||||
|
||||
public static function supports_alpha(string $mime): bool
|
||||
{
|
||||
$is_anim_gif = 0;
|
||||
if (($fh = @fopen($image_filename, 'rb'))) {
|
||||
try {
|
||||
//check if gif is animated (via https://www.php.net/manual/en/function.imagecreatefromgif.php#104473)
|
||||
while (!feof($fh) && $is_anim_gif < 2) {
|
||||
$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);
|
||||
}
|
||||
|
||||
|
||||
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): bool
|
||||
{
|
||||
return in_array(self::normalize_format($format), self::ALPHA_FORMATS);
|
||||
}
|
||||
|
||||
public static function is_input_supported(string $engine, string $format, ?bool $lossless = null): bool
|
||||
{
|
||||
$format = self::normalize_format($format, $lossless);
|
||||
if (!in_array($format, MediaEngine::INPUT_SUPPORT[$engine])) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function is_output_supported(string $engine, string $format, ?bool $lossless = false): bool
|
||||
{
|
||||
$format = self::normalize_format($format, $lossless);
|
||||
if (!in_array($format, MediaEngine::OUTPUT_SUPPORT[$engine])) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Media extension will recognize is returned,
|
||||
* otherwise the incoming format is returned.
|
||||
*
|
||||
* @param $format
|
||||
* @return string|null The format name that the media extension will recognize.
|
||||
*/
|
||||
public static function normalize_format(string $format, ?bool $lossless = null): ?string
|
||||
{
|
||||
if ($format == EXTENSION_WEBP) {
|
||||
if ($lossless === true) {
|
||||
$format = Media::WEBP_LOSSLESS;
|
||||
} else {
|
||||
$format = Media::WEBP_LOSSY;
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists($format, Media::FORMAT_ALIASES)) {
|
||||
return self::FORMAT_ALIASES[$format];
|
||||
}
|
||||
return $format;
|
||||
return MimeType::matches_array($mime, self::ALPHA_FORMATS, true);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -13,65 +13,61 @@ abstract class MediaEngine
|
|||
MediaEngine::IMAGICK,
|
||||
MediaEngine::STATIC,
|
||||
];
|
||||
public const OUTPUT_SUPPORT = [
|
||||
private const OUTPUT_SUPPORT = [
|
||||
MediaEngine::GD => [
|
||||
EXTENSION_GIF,
|
||||
EXTENSION_JPG,
|
||||
EXTENSION_PNG,
|
||||
EXTENSION_WEBP,
|
||||
Media::WEBP_LOSSY,
|
||||
MimeType::GIF,
|
||||
MimeType::JPEG,
|
||||
MimeType::PNG,
|
||||
MimeType::WEBP
|
||||
],
|
||||
MediaEngine::IMAGICK => [
|
||||
EXTENSION_GIF,
|
||||
EXTENSION_JPG,
|
||||
EXTENSION_PNG,
|
||||
EXTENSION_WEBP,
|
||||
Media::WEBP_LOSSY,
|
||||
Media::WEBP_LOSSLESS,
|
||||
MimeType::GIF,
|
||||
MimeType::JPEG,
|
||||
MimeType::PNG,
|
||||
MimeType::WEBP,
|
||||
MimeType::WEBP_LOSSLESS,
|
||||
],
|
||||
MediaEngine::FFMPEG => [
|
||||
EXTENSION_JPG,
|
||||
EXTENSION_WEBP,
|
||||
EXTENSION_PNG,
|
||||
MimeType::JPEG,
|
||||
MimeType::WEBP,
|
||||
MimeType::PNG,
|
||||
],
|
||||
MediaEngine::STATIC => [
|
||||
EXTENSION_JPG,
|
||||
MimeType::JPEG,
|
||||
],
|
||||
];
|
||||
public const INPUT_SUPPORT = [
|
||||
private const INPUT_SUPPORT = [
|
||||
MediaEngine::GD => [
|
||||
EXTENSION_BMP,
|
||||
EXTENSION_GIF,
|
||||
EXTENSION_JPG,
|
||||
EXTENSION_PNG,
|
||||
EXTENSION_WEBP,
|
||||
Media::WEBP_LOSSY,
|
||||
Media::WEBP_LOSSLESS,
|
||||
MimeType::BMP,
|
||||
MimeType::GIF,
|
||||
MimeType::JPEG,
|
||||
MimeType::PNG,
|
||||
MimeType::WEBP,
|
||||
MimeType::WEBP_LOSSLESS,
|
||||
],
|
||||
MediaEngine::IMAGICK => [
|
||||
EXTENSION_BMP,
|
||||
EXTENSION_GIF,
|
||||
EXTENSION_JPG,
|
||||
EXTENSION_PNG,
|
||||
EXTENSION_PSD,
|
||||
EXTENSION_TIFF,
|
||||
EXTENSION_WEBP,
|
||||
Media::WEBP_LOSSY,
|
||||
Media::WEBP_LOSSLESS,
|
||||
EXTENSION_ICO,
|
||||
MimeType::BMP,
|
||||
MimeType::GIF,
|
||||
MimeType::JPEG,
|
||||
MimeType::PNG,
|
||||
MimeType::PSD,
|
||||
MimeType::TIFF,
|
||||
MimeType::WEBP,
|
||||
MimeType::WEBP_LOSSLESS,
|
||||
MimeType::ICO,
|
||||
],
|
||||
MediaEngine::FFMPEG => [
|
||||
EXTENSION_AVI,
|
||||
EXTENSION_MKV,
|
||||
EXTENSION_WEBM,
|
||||
EXTENSION_MP4,
|
||||
EXTENSION_MOV,
|
||||
EXTENSION_FLASH_VIDEO,
|
||||
MimeType::AVI,
|
||||
MimeType::MKV,
|
||||
MimeType::WEBM,
|
||||
MimeType::MP4_VIDEO,
|
||||
MimeType::QUICKTIME,
|
||||
MimeType::FLASH_VIDEO,
|
||||
],
|
||||
MediaEngine::STATIC => [
|
||||
EXTENSION_JPG,
|
||||
EXTENSION_GIF,
|
||||
EXTENSION_PNG,
|
||||
MimeType::JPEG,
|
||||
MimeType::GIF,
|
||||
MimeType::PNG,
|
||||
],
|
||||
];
|
||||
public const RESIZE_TYPE_SUPPORT = [
|
||||
|
@ -92,4 +88,21 @@ abstract class MediaEngine
|
|||
Media::RESIZE_TYPE_FIT
|
||||
]
|
||||
];
|
||||
|
||||
public static function is_output_supported(string $engine, string $mime): bool
|
||||
{
|
||||
return MimeType::matches_array(
|
||||
$mime,
|
||||
MediaEngine::OUTPUT_SUPPORT[$engine],
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
public static function is_input_supported(string $engine, string $mime): bool
|
||||
{
|
||||
return MimeType::matches_array(
|
||||
$mime,
|
||||
MediaEngine::INPUT_SUPPORT[$engine]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,28 +9,6 @@ use function MicroHTML\OPTION;
|
|||
|
||||
class MediaTheme extends Themelet
|
||||
{
|
||||
public function display_form(array $types)
|
||||
{
|
||||
global $page;
|
||||
|
||||
$select = SELECT(["name"=>"media_rescan_type"]);
|
||||
$select->appendChild(OPTION(["value"=>""], "All"));
|
||||
foreach ($types as $type) {
|
||||
$select->appendChild(OPTION(["value"=>$type["ext"]], "{$type["ext"]} ({$type["count"]})"));
|
||||
}
|
||||
|
||||
$html = (string)SHM_SIMPLE_FORM(
|
||||
"admin/media_rescan",
|
||||
"Use this to force scanning for media properties.",
|
||||
TABLE(
|
||||
["class"=>"form"],
|
||||
TR(TH("Image Type"), TD($select)),
|
||||
TR(TD(["colspan"=>"2"], SHM_SUBMIT('Scan Media Information')))
|
||||
)
|
||||
);
|
||||
$page->add_block(new Block("Media Tools", $html));
|
||||
}
|
||||
|
||||
public function get_buttons_html(int $image_id): string
|
||||
{
|
||||
return (string)SHM_SIMPLE_FORM(
|
||||
|
|
105
ext/mime/file_extension.php
Normal file
105
ext/mime/file_extension.php
Normal file
|
@ -0,0 +1,105 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
class FileExtension
|
||||
{
|
||||
public const ANI = 'ani';
|
||||
public const ASC = 'asc';
|
||||
public const ASF = 'asf';
|
||||
public const ASX = 'asx';
|
||||
public const AVI = 'avi';
|
||||
public const BMP = 'bmp';
|
||||
public const BZIP = 'bz';
|
||||
public const BZIP2 = 'bz2';
|
||||
public const CBR = 'cbr';
|
||||
public const CBZ = 'cbz';
|
||||
public const CBT = 'cbt';
|
||||
public const CBA = 'cbA';
|
||||
public const CB7 = 'cb7';
|
||||
public const CSS = 'css';
|
||||
public const CSV = 'csv';
|
||||
public const CUR = 'cur';
|
||||
public const FLASH = 'swf';
|
||||
public const FLASH_VIDEO = 'flv';
|
||||
public const GIF = 'gif';
|
||||
public const GZIP = 'gz';
|
||||
public const HTML = 'html';
|
||||
public const HTM = 'htm';
|
||||
public const ICO = 'ico';
|
||||
public const JFIF = 'jfif';
|
||||
public const JFI = 'jfi';
|
||||
public const JPEG = 'jpeg';
|
||||
public const JPG = 'jpg';
|
||||
public const JS = 'js';
|
||||
public const JSON = 'json';
|
||||
public const MKV = 'mkv';
|
||||
public const MP3 = 'mp3';
|
||||
public const MP4 = 'mp4';
|
||||
public const M4V = 'm4v';
|
||||
public const M4A = 'm4a';
|
||||
public const MPEG = 'mpeg';
|
||||
public const MPG = 'mpg';
|
||||
public const OGG = 'ogg';
|
||||
public const OGG_VIDEO = 'ogv';
|
||||
public const OGG_AUDIO = 'oga';
|
||||
public const PDF = 'pdf';
|
||||
public const PHP = 'php';
|
||||
public const PHP5 = 'php5';
|
||||
public const PNG = 'png';
|
||||
public const PSD = 'psd';
|
||||
public const MOV = 'mov';
|
||||
public const RSS = 'rss';
|
||||
public const SVG = 'svg';
|
||||
public const TAR = 'tar';
|
||||
public const TEXT = 'txt';
|
||||
public const TIFF = 'tiff';
|
||||
public const TIF = 'tif';
|
||||
public const WAV = 'wav';
|
||||
public const WEBM = 'webm';
|
||||
public const WEBP = 'webp';
|
||||
public const WMA = 'wma';
|
||||
public const WMV = 'wmv';
|
||||
public const XML = 'xml';
|
||||
public const XSL = 'xsl';
|
||||
public const ZIP = 'zip';
|
||||
|
||||
/**
|
||||
* Returns the main file extension associated with the specified mimetype.
|
||||
*/
|
||||
public static function get_for_mime(string $mime): ?string
|
||||
{
|
||||
if (empty($mime)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($mime==MimeType::OCTET_STREAM) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = MimeMap::get_for_mime($mime);
|
||||
if ($data!=null) {
|
||||
return $data[MimeMap::MAP_EXT][0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the file extension associated with the specified mimetype.
|
||||
*/
|
||||
public static function get_all_for_mime(string $mime): array
|
||||
{
|
||||
if (empty($mime)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($mime==MimeType::OCTET_STREAM) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$data = MimeMap::get_for_mime($mime);
|
||||
if ($data!=null) {
|
||||
return $data[MimeMap::MAP_EXT];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
14
ext/mime/info.php
Normal file
14
ext/mime/info.php
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
class MimeSystemInfo extends ExtensionInfo
|
||||
{
|
||||
public const KEY = "mime";
|
||||
|
||||
public $key = self::KEY;
|
||||
public $name = "MIME";
|
||||
public $authors = ["Matthew Barbour"=>"matthew@darkholme.net"];
|
||||
public $license = self::LICENSE_WTFPL;
|
||||
public $description = "Provides system mime-related functionality";
|
||||
public $core = true;
|
||||
public $visibility = self::VISIBLE_HIDDEN;
|
||||
}
|
85
ext/mime/main.php
Normal file
85
ext/mime/main.php
Normal file
|
@ -0,0 +1,85 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
require_once "mime_map.php";
|
||||
require_once "file_extension.php";
|
||||
require_once "mime_type.php";
|
||||
|
||||
class MimeSystem extends Extension
|
||||
{
|
||||
/** @var MimeSystemTheme */
|
||||
protected $theme;
|
||||
|
||||
const VERSION = "ext_mime_version";
|
||||
|
||||
public function onParseLinkTemplate(ParseLinkTemplateEvent $event)
|
||||
{
|
||||
$event->replace('$ext', $event->image->get_ext());
|
||||
$event->replace('$mime', $event->image->get_mime());
|
||||
}
|
||||
|
||||
|
||||
public function onDatabaseUpgrade(DatabaseUpgradeEvent $event)
|
||||
{
|
||||
global $database;
|
||||
|
||||
// These upgrades are primarily for initializing mime types on upgrade, and for adjusting mime types whenever an
|
||||
// adjustment needs to be made to the mime types.
|
||||
|
||||
if ($this->get_version(self::VERSION) < 1) {
|
||||
if ($database->transaction) {
|
||||
// Each of these commands could hit a lot of data, combining
|
||||
// them into one big transaction would not be a good idea.
|
||||
$database->commit();
|
||||
}
|
||||
$database->set_timeout(300000); // These updates can take a little bit
|
||||
|
||||
$extensions = $database->get_col_iterable("SELECT DISTINCT ext FROM images");
|
||||
|
||||
foreach ($extensions as $ext) {
|
||||
$mime = MimeType::get_for_extension($ext);
|
||||
|
||||
if (empty($mime) || $mime===MimeType::OCTET_STREAM) {
|
||||
throw new SCoreException("Unknown extension: $ext");
|
||||
}
|
||||
|
||||
$normalized_extension = FileExtension::get_for_mime($mime);
|
||||
|
||||
$database->execute(
|
||||
$database->scoreql_to_sql(
|
||||
"UPDATE images SET mime = :mime, ext = :new_ext WHERE ext = :ext"
|
||||
),
|
||||
["mime" => $mime, "new_ext" => $normalized_extension, "ext" => $ext]
|
||||
);
|
||||
}
|
||||
|
||||
$this->set_version(self::VERSION, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public function onHelpPageBuilding(HelpPageBuildingEvent $event)
|
||||
{
|
||||
if ($event->key===HelpPages::SEARCH) {
|
||||
$block = new Block();
|
||||
$block->header = "File Types";
|
||||
$block->body = $this->theme->get_help_html();
|
||||
$event->add_block($block);
|
||||
}
|
||||
}
|
||||
|
||||
public function onSearchTermParse(SearchTermParseEvent $event)
|
||||
{
|
||||
if (is_null($event->term)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$matches = [];
|
||||
// check for tags first as tag based searches are more common.
|
||||
if (preg_match("/^ext[=|:]([a-zA-Z0-9]+)$/i", $event->term, $matches)) {
|
||||
$ext = strtolower($matches[1]);
|
||||
$event->add_querylet(new Querylet('images.ext = :ext', ["ext" => $ext]));
|
||||
} elseif (preg_match("/^mime[=|:](.+)$/i", $event->term, $matches)) {
|
||||
$mime = strtolower($matches[1]);
|
||||
$event->add_querylet(new Querylet("images.mime = :mime", ["mime"=>$mime]));
|
||||
}
|
||||
}
|
||||
}
|
256
ext/mime/mime_map.php
Normal file
256
ext/mime/mime_map.php
Normal file
|
@ -0,0 +1,256 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
class MimeMap
|
||||
{
|
||||
public const MAP_NAME = 'name';
|
||||
public const MAP_EXT = 'ext';
|
||||
public const MAP_MIME = 'mime';
|
||||
|
||||
// Mime type map. Each entry in the self::ARRAY represents a kind of file, identified by the "correct" mimetype as the key.
|
||||
// The value for each entry is a map of three keys, ext, mime, and name.
|
||||
// ext's value is an array of all of the extensions that the file type can use, with the "correct" one being first.
|
||||
// mime's value is an array of all mime types that the file type is known to use, with the current "correct" one being first.
|
||||
// name's value is a human-readable name for the file format.
|
||||
|
||||
|
||||
private const MAP = [
|
||||
MimeType::ANI => [
|
||||
self::MAP_NAME => "ANI Cursor",
|
||||
self::MAP_EXT => [FileExtension::ANI],
|
||||
self::MAP_MIME => [MimeType::ANI],
|
||||
],
|
||||
MimeType::AVI => [
|
||||
self::MAP_NAME => "AVI",
|
||||
self::MAP_EXT => [FileExtension::AVI],
|
||||
self::MAP_MIME => [MimeType::AVI, 'video/avi', 'video/msvideo'],
|
||||
],
|
||||
MimeType::ASF => [
|
||||
self::MAP_NAME => "ASF/WMV",
|
||||
self::MAP_EXT => [FileExtension::ASF, FileExtension::ASX, FileExtension::WMA, FileExtension::WMV],
|
||||
self::MAP_MIME => [MimeType::ASF, MimeType::WMA, MimeType::WMV],
|
||||
],
|
||||
MimeType::BMP => [
|
||||
self::MAP_NAME => "BMP",
|
||||
self::MAP_EXT => [FileExtension::BMP],
|
||||
self::MAP_MIME => [MimeType::BMP],
|
||||
],
|
||||
MimeType::BZIP => [
|
||||
self::MAP_NAME => "BZIP",
|
||||
self::MAP_EXT => [FileExtension::BZIP],
|
||||
self::MAP_MIME => [MimeType::BZIP],
|
||||
],
|
||||
MimeType::BZIP2 => [
|
||||
self::MAP_NAME => "BZIP2",
|
||||
self::MAP_EXT => [FileExtension::BZIP2],
|
||||
self::MAP_MIME => [MimeType::BZIP2],
|
||||
],
|
||||
MimeType::COMIC_ZIP => [
|
||||
self::MAP_NAME => "CBZ",
|
||||
self::MAP_EXT => [FileExtension::CBZ],
|
||||
self::MAP_MIME => [MimeType::COMIC_ZIP],
|
||||
],
|
||||
MimeType::CSS => [
|
||||
self::MAP_NAME => "Cascading Style Sheet",
|
||||
self::MAP_EXT => [FileExtension::CSS],
|
||||
self::MAP_MIME => [MimeType::CSS],
|
||||
],
|
||||
MimeType::CSV => [
|
||||
self::MAP_NAME => "CSV",
|
||||
self::MAP_EXT => [FileExtension::CSV],
|
||||
self::MAP_MIME => [MimeType::CSV],
|
||||
],
|
||||
MimeType::FLASH => [
|
||||
self::MAP_NAME => "Flash",
|
||||
self::MAP_EXT => [FileExtension::FLASH],
|
||||
self::MAP_MIME => [MimeType::FLASH],
|
||||
],
|
||||
MimeType::FLASH_VIDEO => [
|
||||
self::MAP_NAME => "Flash Video",
|
||||
self::MAP_EXT => [FileExtension::FLASH_VIDEO],
|
||||
self::MAP_MIME => [MimeType::FLASH_VIDEO, 'video/flv'],
|
||||
],
|
||||
MimeType::GIF => [
|
||||
self::MAP_NAME => "GIF",
|
||||
self::MAP_EXT => [FileExtension::GIF],
|
||||
self::MAP_MIME => [MimeType::GIF],
|
||||
],
|
||||
MimeType::GZIP => [
|
||||
self::MAP_NAME => "GZIP",
|
||||
self::MAP_EXT => [FileExtension::GZIP],
|
||||
self::MAP_MIME => [MimeType::TAR],
|
||||
],
|
||||
MimeType::HTML => [
|
||||
self::MAP_NAME => "HTML",
|
||||
self::MAP_EXT => [FileExtension::HTM, FileExtension::HTML],
|
||||
self::MAP_MIME => [MimeType::HTML],
|
||||
],
|
||||
MimeType::ICO => [
|
||||
self::MAP_NAME => "Icon",
|
||||
self::MAP_EXT => [FileExtension::ICO, FileExtension::CUR],
|
||||
self::MAP_MIME => [MimeType::ICO, MimeType::WIN_BITMAP],
|
||||
],
|
||||
MimeType::JPEG => [
|
||||
self::MAP_NAME => "JPEG",
|
||||
self::MAP_EXT => [FileExtension::JPG, FileExtension::JPEG, FileExtension::JFIF, FileExtension::JFI],
|
||||
self::MAP_MIME => [MimeType::JPEG],
|
||||
],
|
||||
MimeType::JS => [
|
||||
self::MAP_NAME => "JavaScript",
|
||||
self::MAP_EXT => [FileExtension::JS],
|
||||
self::MAP_MIME => [MimeType::JS],
|
||||
],
|
||||
MimeType::JSON => [
|
||||
self::MAP_NAME => "JSON",
|
||||
self::MAP_EXT => [FileExtension::JSON],
|
||||
self::MAP_MIME => [MimeType::JSON],
|
||||
],
|
||||
MimeType::MKV => [
|
||||
self::MAP_NAME => "Matroska",
|
||||
self::MAP_EXT => [FileExtension::MKV],
|
||||
self::MAP_MIME => [MimeType::MKV],
|
||||
],
|
||||
MimeType::MP3 => [
|
||||
self::MAP_NAME => "MP3",
|
||||
self::MAP_EXT => [FileExtension::MP3],
|
||||
self::MAP_MIME => [MimeType::MP3],
|
||||
],
|
||||
MimeType::MP4_AUDIO => [
|
||||
self::MAP_NAME => "MP4 Audio",
|
||||
self::MAP_EXT => [FileExtension::M4A],
|
||||
self::MAP_MIME => [MimeType::MP4_AUDIO, "audio/m4a"],
|
||||
],
|
||||
MimeType::MP4_VIDEO => [
|
||||
self::MAP_NAME => "MP4 Video",
|
||||
self::MAP_EXT => [FileExtension::MP4, FileExtension::M4V],
|
||||
self::MAP_MIME => [MimeType::MP4_VIDEO, 'video/x-m4v'],
|
||||
],
|
||||
MimeType::MPEG => [
|
||||
self::MAP_NAME => "MPEG",
|
||||
self::MAP_EXT => [FileExtension::MPG, FileExtension::MPEG],
|
||||
self::MAP_MIME => [MimeType::MPEG],
|
||||
],
|
||||
MimeType::PDF => [
|
||||
self::MAP_NAME => "PDF",
|
||||
self::MAP_EXT => [FileExtension::PDF],
|
||||
self::MAP_MIME => [MimeType::PDF],
|
||||
],
|
||||
MimeType::PHP => [
|
||||
self::MAP_NAME => "PHP",
|
||||
self::MAP_EXT => [FileExtension::PHP, FileExtension::PHP5],
|
||||
self::MAP_MIME => [MimeType::PHP],
|
||||
],
|
||||
MimeType::PNG => [
|
||||
self::MAP_NAME => "PNG",
|
||||
self::MAP_EXT => [FileExtension::PNG],
|
||||
self::MAP_MIME => [MimeType::PNG],
|
||||
],
|
||||
MimeType::PSD => [
|
||||
self::MAP_NAME => "PSD",
|
||||
self::MAP_EXT => [FileExtension::PSD],
|
||||
self::MAP_MIME => [MimeType::PSD],
|
||||
],
|
||||
MimeType::OGG_AUDIO => [
|
||||
self::MAP_NAME => "Ogg Vorbis",
|
||||
self::MAP_EXT => [FileExtension::OGG_AUDIO, FileExtension::OGG],
|
||||
self::MAP_MIME => [MimeType::OGG_AUDIO, MimeType::OGG],
|
||||
],
|
||||
MimeType::OGG_VIDEO => [
|
||||
self::MAP_NAME => "Ogg Theora",
|
||||
self::MAP_EXT => [FileExtension::OGG_VIDEO],
|
||||
self::MAP_MIME => [MimeType::OGG_VIDEO],
|
||||
],
|
||||
MimeType::QUICKTIME => [
|
||||
self::MAP_NAME => "Quicktime",
|
||||
self::MAP_EXT => [FileExtension::MOV],
|
||||
self::MAP_MIME => [MimeType::QUICKTIME],
|
||||
],
|
||||
MimeType::RSS => [
|
||||
self::MAP_NAME => "RSS",
|
||||
self::MAP_EXT => [FileExtension::RSS],
|
||||
self::MAP_MIME => [MimeType::RSS],
|
||||
],
|
||||
MimeType::SVG => [
|
||||
self::MAP_NAME => "SVG",
|
||||
self::MAP_EXT => [FileExtension::SVG],
|
||||
self::MAP_MIME => [MimeType::SVG],
|
||||
],
|
||||
MimeType::TAR => [
|
||||
self::MAP_NAME => "TAR",
|
||||
self::MAP_EXT => [FileExtension::TAR],
|
||||
self::MAP_MIME => [MimeType::TAR],
|
||||
],
|
||||
MimeType::TEXT => [
|
||||
self::MAP_NAME => "Text",
|
||||
self::MAP_EXT => [FileExtension::TEXT, FileExtension::ASC],
|
||||
self::MAP_MIME => [MimeType::TEXT],
|
||||
],
|
||||
MimeType::TIFF => [
|
||||
self::MAP_NAME => "TIFF",
|
||||
self::MAP_EXT => [FileExtension::TIF, FileExtension::TIFF],
|
||||
self::MAP_MIME => [MimeType::TIFF],
|
||||
],
|
||||
MimeType::WAV => [
|
||||
self::MAP_NAME => "Wave",
|
||||
self::MAP_EXT => [FileExtension::WAV],
|
||||
self::MAP_MIME => [MimeType::WAV],
|
||||
],
|
||||
MimeType::WEBM => [
|
||||
self::MAP_NAME => "WebM",
|
||||
self::MAP_EXT => [FileExtension::WEBM],
|
||||
self::MAP_MIME => [MimeType::WEBM],
|
||||
],
|
||||
MimeType::WEBP => [
|
||||
self::MAP_NAME => "WebP",
|
||||
self::MAP_EXT => [FileExtension::WEBP],
|
||||
self::MAP_MIME => [MimeType::WEBP, MimeType::WEBP_LOSSLESS],
|
||||
],
|
||||
MimeType::XML => [
|
||||
self::MAP_NAME => "XML",
|
||||
self::MAP_EXT => [FileExtension::XML],
|
||||
self::MAP_MIME => [MimeType::XML, MimeType::XML_APPLICATION],
|
||||
],
|
||||
MimeType::XSL => [
|
||||
self::MAP_NAME => "XSL",
|
||||
self::MAP_EXT => [FileExtension::XSL],
|
||||
self::MAP_MIME => [MimeType::XSL],
|
||||
],
|
||||
MimeType::ZIP => [
|
||||
self::MAP_NAME => "ZIP",
|
||||
self::MAP_EXT => [FileExtension::ZIP],
|
||||
self::MAP_MIME => [MimeType::ZIP],
|
||||
],
|
||||
];
|
||||
|
||||
public static function get_for_extension(string $ext): ?array
|
||||
{
|
||||
$ext = strtolower($ext);
|
||||
|
||||
foreach (self::MAP as $key => $value) {
|
||||
if (in_array($ext, $value[self::MAP_EXT])) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function get_for_mime(string $mime): ?array
|
||||
{
|
||||
$mime = strtolower(MimeType::remove_parameters($mime));
|
||||
|
||||
foreach (self::MAP as $key => $value) {
|
||||
if (in_array($mime, $value[self::MAP_MIME])) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function get_name_for_mime(string $mime): ?string
|
||||
{
|
||||
$data = self::get_for_mime($mime);
|
||||
if ($data!==null) {
|
||||
return $data[self::MAP_NAME];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
254
ext/mime/mime_type.php
Normal file
254
ext/mime/mime_type.php
Normal file
|
@ -0,0 +1,254 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
require_once "file_extension.php";
|
||||
|
||||
class MimeType
|
||||
{
|
||||
// Couldn't find a mimetype for ani, so made one up based on it being a riff container
|
||||
public const ANI = 'application/riff+ani';
|
||||
public const ASF = 'video/x-ms-asf';
|
||||
public const AVI = 'video/x-msvideo';
|
||||
// Went with mime types from http://fileformats.archiveteam.org/wiki/Comic_Book_Archive
|
||||
public const COMIC_ZIP = 'application/vnd.comicbook+zip';
|
||||
public const COMIC_RAR = 'application/vnd.comicbook-rar';
|
||||
public const BMP = 'image/x-ms-bmp';
|
||||
public const BZIP = 'application/x-bzip';
|
||||
public const BZIP2 = 'application/x-bzip2';
|
||||
public const CSS = 'text/css';
|
||||
public const CSV = 'text/csv';
|
||||
public const FLASH = 'application/x-shockwave-flash';
|
||||
public const FLASH_VIDEO = 'video/x-flv';
|
||||
public const GIF = 'image/gif';
|
||||
public const GZIP = 'application/x-gzip';
|
||||
public const HTML = 'text/html';
|
||||
public const ICO = 'image/x-icon';
|
||||
public const JPEG = 'image/jpeg';
|
||||
public const JS = 'text/javascript';
|
||||
public const JSON = 'application/json';
|
||||
public const MKV = 'video/x-matroska';
|
||||
public const MP3 = 'audio/mpeg';
|
||||
public const MP4_AUDIO = 'audio/mp4';
|
||||
public const MP4_VIDEO = 'video/mp4';
|
||||
public const MPEG = 'video/mpeg';
|
||||
public const OCTET_STREAM = 'application/octet-stream';
|
||||
public const OGG = 'application/ogg';
|
||||
public const OGG_VIDEO = 'video/ogg';
|
||||
public const OGG_AUDIO = 'audio/ogg';
|
||||
public const PDF = 'application/pdf';
|
||||
public const PHP = 'text/x-php';
|
||||
public const PNG = 'image/png';
|
||||
public const PSD = 'image/vnd.adobe.photoshop';
|
||||
public const QUICKTIME = 'video/quicktime';
|
||||
public const RSS = 'application/rss+xml';
|
||||
public const SVG = 'image/svg+xml';
|
||||
public const TAR = 'application/x-tar';
|
||||
public const TEXT = 'text/plain';
|
||||
public const TIFF = 'image/tiff';
|
||||
public const WAV = 'audio/x-wav';
|
||||
public const WEBM = 'video/webm';
|
||||
public const WEBP = 'image/webp';
|
||||
public const WEBP_LOSSLESS = self::WEBP."; ".self::LOSSLESS_PARAMETER;
|
||||
public const WIN_BITMAP = 'image/x-win-bitmap';
|
||||
public const WMA = 'audio/x-ms-wma';
|
||||
public const WMV = 'video/x-ms-wmv';
|
||||
public const XML = 'text/xml';
|
||||
public const XML_APPLICATION = 'application/xml';
|
||||
public const XSL = 'application/xsl+xml';
|
||||
public const ZIP = 'application/zip';
|
||||
|
||||
public const LOSSLESS_PARAMETER = "lossless=true";
|
||||
|
||||
public const CHARSET_UTF8 = "charset=utf-8";
|
||||
|
||||
//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];
|
||||
|
||||
private const REGEX_MIME_TYPE = "/^([-\w.]+)\/([-\w.]+)(;.+)?$/";
|
||||
|
||||
public static function is_mime(string $value): bool
|
||||
{
|
||||
return preg_match(self::REGEX_MIME_TYPE, $value)===1;
|
||||
}
|
||||
|
||||
public static function add_parameters(String $mime, String...$parameters): string
|
||||
{
|
||||
if (empty($parameters)) {
|
||||
return $mime;
|
||||
}
|
||||
return $mime."; ".join("; ", $parameters);
|
||||
}
|
||||
|
||||
public static function remove_parameters(string $mime): string
|
||||
{
|
||||
$i = strpos($mime, ";");
|
||||
if ($i!==false) {
|
||||
return substr($mime, 0, $i);
|
||||
}
|
||||
return $mime;
|
||||
}
|
||||
|
||||
public static function matches_array(string $mime, array $mime_array, bool $exact = false): bool
|
||||
{
|
||||
// If there's an exact match, find it and that's it
|
||||
if (in_array($mime, $mime_array)) {
|
||||
return true;
|
||||
}
|
||||
if ($exact) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$mime = self::remove_parameters($mime);
|
||||
|
||||
return in_array($mime, $mime_array);
|
||||
}
|
||||
|
||||
public static function matches(string $mime1, string $mime2, bool $exact = false): bool
|
||||
{
|
||||
if (!$exact) {
|
||||
$mime1 = self::remove_parameters($mime1);
|
||||
$mime2 = self::remove_parameters($mime2);
|
||||
}
|
||||
return strtolower($mime1)===strtolower($mime2);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines if a file is an animated gif.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
public static function is_animated_gif(string $image_filename): bool
|
||||
{
|
||||
$is_anim_gif = 0;
|
||||
if (($fh = @fopen($image_filename, 'rb'))) {
|
||||
try {
|
||||
//check if gif is animated (via https://www.php.net/manual/en/function.imagecreatefromgif.php#104473)
|
||||
while (!feof($fh) && $is_anim_gif < 2) {
|
||||
$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);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the mimetype that matches the provided extension.
|
||||
*/
|
||||
public static function get_for_extension(string $ext): ?string
|
||||
{
|
||||
$data = MimeMap::get_for_extension($ext);
|
||||
if ($data!=null) {
|
||||
return $data[MimeMap::MAP_MIME][0];
|
||||
}
|
||||
// This was an old solution for differentiating lossless webps
|
||||
if ($ext==="webp-lossless") {
|
||||
return MimeType::WEBP_LOSSLESS;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mimetype for the specified file via file inspection
|
||||
* @param String $file
|
||||
* @return String The mimetype that was found. Returns generic octet binary mimetype if not found.
|
||||
*/
|
||||
public static function get_for_file(string $file, ?string $ext = null): string
|
||||
{
|
||||
if (!file_exists($file)) {
|
||||
throw new SCoreException("File not found: ".$file);
|
||||
}
|
||||
|
||||
$output = self::OCTET_STREAM;
|
||||
|
||||
$type = false;
|
||||
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
try {
|
||||
$type = finfo_file($finfo, $file);
|
||||
} finally {
|
||||
finfo_close($finfo);
|
||||
}
|
||||
|
||||
if ($type !== false && !empty($type)) {
|
||||
$output = $type;
|
||||
}
|
||||
|
||||
if (!empty($ext)) {
|
||||
// Here we handle the few file types that need extension-based handling
|
||||
$ext = strtolower($ext);
|
||||
if ($type===MimeType::ZIP && $ext===FileExtension::CBZ) {
|
||||
$output = MimeType::COMIC_ZIP;
|
||||
}
|
||||
if ($type===MimeType::OCTET_STREAM) {
|
||||
switch ($ext) {
|
||||
case FileExtension::ANI:
|
||||
$output = MimeType::ANI;
|
||||
break;
|
||||
// TODO: There is no uniquely defined Mime type for the cursor format. Need to figure this out.
|
||||
// case FileExtension::CUR:
|
||||
// $output = MimeType::CUR;
|
||||
// break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Implement manual byte inspections for supported esoteric formats, like ANI
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
9
ext/mime/test.php
Normal file
9
ext/mime/test.php
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?php declare(strict_types=1);
|
||||
class MimeSystemTest extends ShimmiePHPUnitTestCase
|
||||
{
|
||||
public function testJPEG()
|
||||
{
|
||||
$result = MimeType::get_for_file("tests/bedroom_workshop.jpg");
|
||||
$this->assertEquals($result, MimeType::JPEG);
|
||||
}
|
||||
}
|
41
ext/mime/theme.php
Normal file
41
ext/mime/theme.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
class MimeSystemTheme extends Themelet
|
||||
{
|
||||
public function get_help_html()
|
||||
{
|
||||
$mimes = DataHandlerExtension::get_all_supported_mimes();
|
||||
sort($mimes);
|
||||
$exts = [];
|
||||
foreach ($mimes as $mime) {
|
||||
$exts[] = FileExtension::get_for_mime($mime);
|
||||
}
|
||||
$mimes = join("</li><li>", $mimes);
|
||||
sort($exts);
|
||||
$exts = join("</li><li>", $exts);
|
||||
|
||||
return '<p>Search for images by extension</p>
|
||||
|
||||
<div class="command_example">
|
||||
<pre>ext=jpg</pre>
|
||||
<p>Returns images with the extension "jpg".</p>
|
||||
</div>
|
||||
|
||||
These extensions are available in the system:
|
||||
<ul><li>'.$exts.'</li></ul>
|
||||
|
||||
<hr/>
|
||||
|
||||
<p>Search for images by MIME type</p>
|
||||
|
||||
<div class="command_example">
|
||||
<pre>mime=image/jpeg</pre>
|
||||
<p>Returns images that have the MIME type "image/jpeg".</p>
|
||||
</div>
|
||||
|
||||
These MIME types are available in the system:
|
||||
<ul><li>'.$mimes.'</li></ul>
|
||||
|
||||
';
|
||||
}
|
||||
}
|
|
@ -174,7 +174,7 @@ class _SafeOuroborosImage
|
|||
// file
|
||||
$this->height = intval($img->height);
|
||||
$this->width = intval($img->width);
|
||||
$this->file_ext = $img->ext;
|
||||
$this->file_ext = $img->get_ext();
|
||||
$this->file_size = intval($img->filesize);
|
||||
$this->file_url = make_http($img->get_image_link());
|
||||
$this->md5 = $img->hash;
|
||||
|
@ -374,9 +374,9 @@ class OuroborosAPI extends Extension
|
|||
$this->event = $event;
|
||||
$this->type = $matches[1];
|
||||
if ($this->type == 'json') {
|
||||
$page->set_type('application/json; charset=utf-8');
|
||||
$page->set_mime('application/json; charset=utf-8');
|
||||
} elseif ($this->type == 'xml') {
|
||||
$page->set_type('text/xml; charset=utf-8');
|
||||
$page->set_mime('text/xml; charset=utf-8');
|
||||
}
|
||||
$page->set_mode(PageMode::DATA);
|
||||
$this->tryAuth();
|
||||
|
|
|
@ -7,6 +7,6 @@ class QRImage extends Extension
|
|||
|
||||
public function onDisplayingImage(DisplayingImageEvent $event)
|
||||
{
|
||||
$this->theme->links_block(make_http(make_link('image/'.$event->image->id.'.'.$event->image->ext)));
|
||||
$this->theme->links_block(make_http(make_link('image/'.$event->image->id.'.'.$event->image->get_ext())));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,13 +29,13 @@ class RandomImage extends Extension
|
|||
|
||||
if ($action === "download") {
|
||||
if (!is_null($image)) {
|
||||
send_event(new ImageDownloadingEvent($image, $image->get_image_filename(), $image->get_mime_type()));
|
||||
send_event(new ImageDownloadingEvent($image, $image->get_image_filename(), $image->get_mime()));
|
||||
}
|
||||
} elseif ($action === "view") {
|
||||
send_event(new DisplayingImageEvent($image));
|
||||
} elseif ($action === "widget") {
|
||||
$page->set_mode(PageMode::DATA);
|
||||
$page->set_type(MIME_TYPE_HTML);
|
||||
$page->set_mime(MimeType::HTML);
|
||||
$page->set_data($this->theme->build_thumb_html($image));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,10 @@ class RegenThumb extends Extension
|
|||
/** @var RegenThumbTheme */
|
||||
protected $theme;
|
||||
|
||||
public function regenerate_thumbnail($image, $force = true): bool
|
||||
public function regenerate_thumbnail(Image $image, bool $force = true): bool
|
||||
{
|
||||
global $cache;
|
||||
$event = new ThumbnailGenerationEvent($image->hash, $image->ext, $force);
|
||||
$event = new ThumbnailGenerationEvent($image->hash, $image->get_mime(), $force);
|
||||
send_event($event);
|
||||
$cache->delete("thumb-block:{$image->id}");
|
||||
return $event->generated;
|
||||
|
@ -109,11 +109,11 @@ class RegenThumb extends Extension
|
|||
$limit=intval($_POST["regen_thumb_limit"]);
|
||||
}
|
||||
|
||||
$type = "";
|
||||
if (isset($_POST["regen_thumb_limit"])) {
|
||||
$type = $_POST["regen_thumb_type"];
|
||||
$mime = "";
|
||||
if (isset($_POST["regen_thumb_mime"])) {
|
||||
$mime = $_POST["regen_thumb_mime"];
|
||||
}
|
||||
$images = $this->get_images($type);
|
||||
$images = $this->get_images($mime);
|
||||
|
||||
$i = 0;
|
||||
foreach ($images as $image) {
|
||||
|
@ -123,7 +123,7 @@ class RegenThumb extends Extension
|
|||
continue;
|
||||
}
|
||||
}
|
||||
$event = new ThumbnailGenerationEvent($image["hash"], $image["ext"], $force);
|
||||
$event = new ThumbnailGenerationEvent($image["hash"], $image["mime"], $force);
|
||||
send_event($event);
|
||||
if ($event->generated) {
|
||||
$i++;
|
||||
|
@ -137,8 +137,8 @@ class RegenThumb extends Extension
|
|||
case "delete_thumbs":
|
||||
$event->redirect = true;
|
||||
|
||||
if (isset($_POST["delete_thumb_type"])&&$_POST["delete_thumb_type"]!="") {
|
||||
$images = $this->get_images($_POST["delete_thumb_type"]);
|
||||
if (isset($_POST["delete_thumb_mime"])&&$_POST["delete_thumb_mime"]!="") {
|
||||
$images = $this->get_images($_POST["delete_thumb_mime"]);
|
||||
|
||||
$i = 0;
|
||||
foreach ($images as $image) {
|
||||
|
@ -148,7 +148,7 @@ class RegenThumb extends Extension
|
|||
$i++;
|
||||
}
|
||||
}
|
||||
$page->flash("Deleted $i thumbnails for ".$_POST["delete_thumb_type"]." images");
|
||||
$page->flash("Deleted $i thumbnails for ".$_POST["delete_thumb_mime"]." images");
|
||||
} else {
|
||||
$dir = "data/thumbs/";
|
||||
$this->remove_dir_recursively($dir);
|
||||
|
@ -160,15 +160,15 @@ class RegenThumb extends Extension
|
|||
}
|
||||
}
|
||||
|
||||
public function get_images(String $ext = null)
|
||||
public function get_images(String $mime = null)
|
||||
{
|
||||
global $database;
|
||||
|
||||
$query = "SELECT hash, ext FROM images";
|
||||
$query = "SELECT hash, mime FROM images";
|
||||
$args = [];
|
||||
if ($ext!=null&&$ext!="") {
|
||||
$query .= " WHERE ext = :ext";
|
||||
$args["ext"] = $ext;
|
||||
if (!empty($mime)) {
|
||||
$query .= " WHERE mime = :mime";
|
||||
$args["mime"] = $mime;
|
||||
}
|
||||
|
||||
return $database->get_all($query, $args);
|
||||
|
|
|
@ -36,10 +36,10 @@ class RegenThumbTheme extends Themelet
|
|||
{
|
||||
global $page, $database;
|
||||
|
||||
$types = [];
|
||||
$results = $database->get_all("SELECT ext, count(*) count FROM images group by ext");
|
||||
$mimes = [];
|
||||
$results = $database->get_all("SELECT mime, count(*) count FROM images group by mime");
|
||||
foreach ($results as $result) {
|
||||
array_push($types, "<option value='".$result["ext"]."'>".$result["ext"]." (".$result["count"].")</option>");
|
||||
array_push($mimes, "<option value='".$result["mime"]."'>".$result["mime"]." (".$result["count"].")</option>");
|
||||
}
|
||||
|
||||
$html = "
|
||||
|
@ -48,10 +48,10 @@ class RegenThumbTheme extends Themelet
|
|||
<table class='form'>
|
||||
<tr><th><label for='regen_thumb_force'>Force</label></th><td><input type='checkbox' name='regen_thumb_force' id='regen_thumb_force' value='true' /></td></tr>
|
||||
<tr><th><label for='regen_thumb_limit'>Limit</label></th><td><input type='number' name='regen_thumb_limit' id='regen_thumb_limit' value='1000' /></td></tr>
|
||||
<tr><th><label for='regen_thumb_type'>Type</label></th><td>
|
||||
<select name='regen_thumb_type' id='regen_thumb_type'>
|
||||
<tr><th><label for='regen_thumb_mime'>MIME</label></th><td>
|
||||
<select name='regen_thumb_mime' id='regen_thumb_mime'>
|
||||
<option value=''>All</option>
|
||||
".implode($types)."
|
||||
".implode($mimes)."
|
||||
</select>
|
||||
</td></tr>
|
||||
<tr><td colspan='2'><input type='submit' value='Regenerate Thumbnails'></td></tr>
|
||||
|
@ -59,10 +59,10 @@ class RegenThumbTheme extends Themelet
|
|||
</form></p>
|
||||
<p>".make_form(make_link("admin/delete_thumbs"), "POST", false, "", "return confirm('Are you sure you want to delete all thumbnails?')")."
|
||||
<table class='form'>
|
||||
<tr><th><label for='delete_thumb_type'>Type</label></th><td>
|
||||
<select name='delete_thumb_type' id='delete_thumb_type'>
|
||||
<tr><th><label for='delete_thumb_mime'>MIME</label></th><td>
|
||||
<select name='delete_thumb_mime' id='delete_thumb_mime'>
|
||||
<option value=''>All</option>
|
||||
".implode($types)."
|
||||
".implode($mimes)."
|
||||
</select>
|
||||
</td></tr>
|
||||
<tr><td colspan='2'><input type='submit' value='Delete Thumbnails'></td></tr>
|
||||
|
|
|
@ -40,7 +40,7 @@ class ResizeImage extends Extension
|
|||
{
|
||||
global $user, $config;
|
||||
if ($user->can(Permissions::EDIT_FILES) && $config->get_bool(ResizeConfig::ENABLED)
|
||||
&& $this->can_resize_format($event->image->ext, $event->image->lossless)) {
|
||||
&& $this->can_resize_mime($event->image->get_mime())) {
|
||||
/* Add a link to resize the image */
|
||||
$event->add_part($this->theme->get_resize_html($event->image));
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ class ResizeImage extends Extension
|
|||
$image_obj = Image::by_id($event->image_id);
|
||||
|
||||
if ($config->get_bool(ResizeConfig::UPLOAD) == true
|
||||
&& $this->can_resize_format($event->type, $image_obj->lossless)) {
|
||||
&& $this->can_resize_mime($event->mime)) {
|
||||
$width = $height = 0;
|
||||
|
||||
if ($config->get_int(ResizeConfig::DEFAULT_WIDTH) !== 0) {
|
||||
|
@ -84,7 +84,7 @@ class ResizeImage extends Extension
|
|||
$height = $config->get_int(ResizeConfig::DEFAULT_HEIGHT);
|
||||
}
|
||||
$isanigif = 0;
|
||||
if ($image_obj->ext == EXTENSION_GIF) {
|
||||
if ($image_obj->get_mime() == MimeType::GIF) {
|
||||
$image_filename = warehouse_path(Image::IMAGE_DIR, $image_obj->hash);
|
||||
if (($fh = @fopen($image_filename, 'rb'))) {
|
||||
//check if gif is animated (via https://www.php.net/manual/en/function.imagecreatefromgif.php#104473)
|
||||
|
@ -104,7 +104,7 @@ class ResizeImage extends Extension
|
|||
//Need to generate thumbnail again...
|
||||
//This only seems to be an issue if one of the sizes was set to 0.
|
||||
$image_obj = Image::by_id($event->image_id); //Must be a better way to grab the new hash than setting this again..
|
||||
send_event(new ThumbnailGenerationEvent($image_obj->hash, $image_obj->ext, true));
|
||||
send_event(new ThumbnailGenerationEvent($image_obj->hash, $image_obj->get_mime(), true));
|
||||
|
||||
log_info("resize", "Image #{$event->image_id} has been resized to: ".$width."x".$height);
|
||||
//TODO: Notify user that image has been resized.
|
||||
|
@ -161,12 +161,12 @@ class ResizeImage extends Extension
|
|||
}
|
||||
}
|
||||
|
||||
private function can_resize_format($format, ?bool $lossless = null): bool
|
||||
private function can_resize_mime($mime): bool
|
||||
{
|
||||
global $config;
|
||||
$engine = $config->get_string(ResizeConfig::ENGINE);
|
||||
return Media::is_input_supported($engine, $format, $lossless)
|
||||
&& Media::is_output_supported($engine, $format, $lossless);
|
||||
return MediaEngine::is_input_supported($engine, $mime)
|
||||
&& MediaEngine::is_output_supported($engine, $mime);
|
||||
}
|
||||
|
||||
|
||||
|
@ -183,7 +183,7 @@ class ResizeImage extends Extension
|
|||
$engine = $config->get_string(ResizeConfig::ENGINE);
|
||||
|
||||
|
||||
if (!$this->can_resize_format($image_obj->ext, $image_obj->lossless)) {
|
||||
if (!$this->can_resize_mime($image_obj->get_mime())) {
|
||||
throw new ImageResizeException("Engine $engine cannot resize selected image");
|
||||
}
|
||||
|
||||
|
@ -206,11 +206,11 @@ class ResizeImage extends Extension
|
|||
send_event(new MediaResizeEvent(
|
||||
$engine,
|
||||
$image_filename,
|
||||
$image_obj->ext,
|
||||
$image_obj->get_mime(),
|
||||
$tmp_filename,
|
||||
$new_width,
|
||||
$new_height,
|
||||
true
|
||||
Media::RESIZE_TYPE_STRETCH
|
||||
));
|
||||
|
||||
$new_image = new Image();
|
||||
|
@ -219,7 +219,6 @@ class ResizeImage extends Extension
|
|||
$new_image->filename = 'resized-'.$image_obj->filename;
|
||||
$new_image->width = $new_width;
|
||||
$new_image->height = $new_height;
|
||||
$new_image->ext = $image_obj->ext;
|
||||
|
||||
/* Move the new image into the main storage location */
|
||||
$target = warehouse_path(Image::IMAGE_DIR, $new_image->hash);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
// TODO Add warning that rotate doesn't support lossless webp output
|
||||
|
||||
/**
|
||||
* This class is just a wrapper around SCoreException.
|
||||
*/
|
||||
|
@ -15,7 +17,7 @@ class RotateImage extends Extension
|
|||
/** @var RotateImageTheme */
|
||||
protected $theme;
|
||||
|
||||
const SUPPORTED_MIME = [MIME_TYPE_JPEG, MIME_TYPE_PNG, MIME_TYPE_GIF, MIME_TYPE_WEBP];
|
||||
const SUPPORTED_MIME = [MimeType::JPEG, MimeType::PNG, MimeType::GIF, MimeType::WEBP];
|
||||
|
||||
public function onInitExt(InitExtEvent $event)
|
||||
{
|
||||
|
@ -28,7 +30,7 @@ class RotateImage extends Extension
|
|||
{
|
||||
global $user, $config;
|
||||
if ($user->can(Permissions::EDIT_FILES) && $config->get_bool("rotate_enabled")
|
||||
&& in_array(get_mime_for_extension($event->image->ext), self::SUPPORTED_MIME)) {
|
||||
&& MimeType::matches_array($event->image->get_mime(), self::SUPPORTED_MIME)) {
|
||||
/* Add a link to rotate the image */
|
||||
$event->add_part($this->theme->get_rotate_html($event->image->id));
|
||||
}
|
||||
|
@ -126,28 +128,6 @@ class RotateImage extends Extension
|
|||
throw new ImageRotateException("Could not load image: ".$image_filename);
|
||||
}
|
||||
|
||||
/* Rotate and resample the image */
|
||||
/*
|
||||
$image_rotated = imagecreatetruecolor( $new_width, $new_height );
|
||||
|
||||
if ( ($info[2] == IMAGETYPE_GIF) || ($info[2] == IMAGETYPE_PNG) ) {
|
||||
$transparency = imagecolortransparent($image);
|
||||
|
||||
if ($transparency >= 0) {
|
||||
$transparent_color = imagecolorsforindex($image, $trnprt_indx);
|
||||
$transparency = imagecolorallocate($image_rotated, $trnprt_color['red'], $trnprt_color['green'], $trnprt_color['blue']);
|
||||
imagefill($image_rotated, 0, 0, $transparency);
|
||||
imagecolortransparent($image_rotated, $transparency);
|
||||
}
|
||||
elseif ($info[2] == IMAGETYPE_PNG) {
|
||||
imagealphablending($image_rotated, false);
|
||||
$color = imagecolorallocatealpha($image_rotated, 0, 0, 0, 127);
|
||||
imagefill($image_rotated, 0, 0, $color);
|
||||
imagesavealpha($image_rotated, true);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
$background_color = 0;
|
||||
switch ($info[2]) {
|
||||
case IMAGETYPE_PNG:
|
||||
|
@ -193,7 +173,6 @@ class RotateImage extends Extension
|
|||
$new_image->filename = 'rotated-'.$image_obj->filename;
|
||||
$new_image->width = $new_width;
|
||||
$new_image->height = $new_height;
|
||||
$new_image->ext = $image_obj->ext;
|
||||
|
||||
/* Move the new image into the main storage location */
|
||||
$target = warehouse_path(Image::IMAGE_DIR, $new_image->hash);
|
||||
|
|
|
@ -16,7 +16,7 @@ class RSSComments extends Extension
|
|||
global $config, $database, $page;
|
||||
if ($event->page_matches("rss/comments")) {
|
||||
$page->set_mode(PageMode::DATA);
|
||||
$page->set_type(MIME_TYPE_RSS);
|
||||
$page->set_mime(MimeType::RSS);
|
||||
|
||||
$comments = $database->get_all("
|
||||
SELECT
|
||||
|
|
|
@ -9,7 +9,7 @@ class RSSCommentsTest extends ShimmiePHPUnitTestCase
|
|||
send_event(new CommentPostingEvent($image_id, $user, "ASDFASDF"));
|
||||
|
||||
$this->get_page('rss/comments');
|
||||
//$this->assert_mime(MIME_TYPE_RSS);
|
||||
//$this->assert_mime(MimeType::RSS);
|
||||
$this->assert_no_content("Exception");
|
||||
$this->assert_content("ASDFASDF");
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ class RSSImages extends Extension
|
|||
global $page;
|
||||
global $config;
|
||||
$page->set_mode(PageMode::DATA);
|
||||
$page->set_type(MIME_TYPE_RSS);
|
||||
$page->set_mime(MimeType::RSS);
|
||||
|
||||
$data = "";
|
||||
foreach ($images as $image) {
|
||||
|
|
|
@ -8,26 +8,26 @@ class RSSImagesTest extends ShimmiePHPUnitTestCase
|
|||
$this->log_out();
|
||||
|
||||
$this->get_page('rss/images');
|
||||
//$this->assert_mime(MIME_TYPE_RSS);
|
||||
//$this->assert_mime(MimeType::RSS);
|
||||
$this->assert_no_content("Exception");
|
||||
|
||||
$this->get_page('rss/images/1');
|
||||
//$this->assert_mime(MIME_TYPE_RSS);
|
||||
//$this->assert_mime(MimeType::RSS);
|
||||
$this->assert_no_content("Exception");
|
||||
|
||||
# FIXME: test that the image is actually found
|
||||
$this->get_page('rss/images/computer/1');
|
||||
//$this->assert_mime(MIME_TYPE_RSS);
|
||||
//$this->assert_mime(MimeType::RSS);
|
||||
$this->assert_no_content("Exception");
|
||||
|
||||
# valid tag, invalid page
|
||||
$this->get_page('rss/images/computer/2');
|
||||
//$this->assert_mime(MIME_TYPE_RSS);
|
||||
//$this->assert_mime(MimeType::RSS);
|
||||
$this->assert_no_content("Exception");
|
||||
|
||||
# not found
|
||||
$this->get_page('rss/images/waffle/2');
|
||||
//$this->assert_mime(MIME_TYPE_RSS);
|
||||
//$this->assert_mime(MimeType::RSS);
|
||||
$this->assert_no_content("Exception");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ class _SafeImage
|
|||
public $hash;
|
||||
public $filesize;
|
||||
public $ext;
|
||||
public $mime;
|
||||
public $posted;
|
||||
public $source;
|
||||
public $owner_id;
|
||||
|
@ -20,7 +21,8 @@ class _SafeImage
|
|||
$this->width = $img->width;
|
||||
$this->hash = $img->hash;
|
||||
$this->filesize = $img->filesize;
|
||||
$this->ext = $img->ext;
|
||||
$this->ext = $img->get_ext();
|
||||
$this->mime = $img->get_mime();
|
||||
$this->posted = strtotime($img->posted);
|
||||
$this->source = $img->source;
|
||||
$this->owner_id = $img->owner_id;
|
||||
|
@ -36,7 +38,7 @@ class ShimmieApi extends Extension
|
|||
|
||||
if ($event->page_matches("api/shimmie")) {
|
||||
$page->set_mode(PageMode::DATA);
|
||||
$page->set_type(MIME_TYPE_TEXT);
|
||||
$page->set_mime(MimeType::TEXT);
|
||||
|
||||
if ($event->page_matches("api/shimmie/get_tags")) {
|
||||
if ($event->count_args() > 0) {
|
||||
|
|
|
@ -155,7 +155,7 @@ class XMLSitemap extends Extension
|
|||
// Generate new sitemap
|
||||
file_put_contents($this->sitemap_filepath, $xml);
|
||||
$page->set_mode(PageMode::DATA);
|
||||
$page->set_type(MIME_TYPE_XML_APPLICATION);
|
||||
$page->set_mime(MimeType::XML_APPLICATION);
|
||||
$page->set_data($xml);
|
||||
}
|
||||
|
||||
|
@ -191,7 +191,7 @@ class XMLSitemap extends Extension
|
|||
$xml = file_get_contents($this->sitemap_filepath);
|
||||
|
||||
$page->set_mode(PageMode::DATA);
|
||||
$page->set_type(MIME_TYPE_XML_APPLICATION);
|
||||
$page->set_mime(MimeType::XML_APPLICATION);
|
||||
$page->set_data($xml);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ class StaticFiles extends Extension
|
|||
$page->set_mode(PageMode::DATA);
|
||||
$page->set_data(file_get_contents($filename));
|
||||
|
||||
$page->set_type(get_mime($filename));
|
||||
$page->set_mime(MimeType::get_for_file($filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ class TagList extends Extension
|
|||
}
|
||||
|
||||
$page->set_mode(PageMode::DATA);
|
||||
$page->set_type(MIME_TYPE_TEXT);
|
||||
$page->set_mime(MimeType::TEXT);
|
||||
$page->set_data(implode("\n", $res));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ class TaggerXML extends Extension
|
|||
"</tags>";
|
||||
|
||||
$page->set_mode(PageMode::DATA);
|
||||
$page->set_type(MIME_TYPE_XML);
|
||||
$page->set_mime(MimeType::XML);
|
||||
$page->set_data($xml);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
class TranscodeConfig
|
||||
{
|
||||
const VERSION = "ext_transcode_version";
|
||||
const ENGINE = "transcode_engine";
|
||||
const ENABLED = "transcode_enabled";
|
||||
const GET_ENABLED = "transcode_get_enabled";
|
||||
|
|
|
@ -16,23 +16,23 @@ class TranscodeImage extends Extension
|
|||
|
||||
const ACTION_BULK_TRANSCODE = "bulk_transcode";
|
||||
|
||||
const INPUT_FORMATS = [
|
||||
"BMP" => EXTENSION_BMP,
|
||||
"GIF" => EXTENSION_GIF,
|
||||
"ICO" => EXTENSION_ICO,
|
||||
"JPG" => EXTENSION_JPG,
|
||||
"PNG" => EXTENSION_PNG,
|
||||
"PSD" => EXTENSION_PSD,
|
||||
"TIFF" => EXTENSION_TIFF,
|
||||
"WEBP" => EXTENSION_WEBP,
|
||||
const INPUT_MIMES = [
|
||||
"BMP" => MimeType::BMP,
|
||||
"GIF" => MimeType::GIF,
|
||||
"ICO" => MimeType::ICO,
|
||||
"JPG" => MimeType::JPEG,
|
||||
"PNG" => MimeType::PNG,
|
||||
"PSD" => MimeType::PSD,
|
||||
"TIFF" => MimeType::TIFF,
|
||||
"WEBP" => MimeType::WEBP
|
||||
];
|
||||
|
||||
const OUTPUT_FORMATS = [
|
||||
const OUTPUT_MIMES = [
|
||||
"" => "",
|
||||
"JPEG (lossy)" => EXTENSION_JPG,
|
||||
"PNG (lossless)" => EXTENSION_PNG,
|
||||
"WEBP (lossy)" => Media::WEBP_LOSSY,
|
||||
"WEBP (lossless)" => Media::WEBP_LOSSLESS,
|
||||
"JPEG (lossy)" => MimeType::JPEG,
|
||||
"PNG (lossless)" => MimeType::PNG,
|
||||
"WEBP (lossy)" => MimeType::WEBP,
|
||||
"WEBP (lossless)" => MimeType::WEBP_LOSSLESS,
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -52,19 +52,82 @@ class TranscodeImage extends Extension
|
|||
$config->set_default_string(TranscodeConfig::ENGINE, MediaEngine::GD);
|
||||
$config->set_default_int(TranscodeConfig::QUALITY, 80);
|
||||
|
||||
foreach (array_values(self::INPUT_FORMATS) as $format) {
|
||||
$config->set_default_string(TranscodeConfig::UPLOAD_PREFIX.$format, "");
|
||||
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)
|
||||
{
|
||||
global $config;
|
||||
|
||||
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)) {
|
||||
$engine = $config->get_string(TranscodeConfig::ENGINE);
|
||||
if ($this->can_convert_format($engine, $event->image->ext, $event->image->lossless)) {
|
||||
$options = $this->get_supported_output_formats($engine, $event->image->ext, $event->image->lossless??false);
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -82,10 +145,10 @@ class TranscodeImage extends Extension
|
|||
$sb->add_bool_option(TranscodeConfig::ENABLED, "Allow transcoding images: ", true);
|
||||
$sb->add_bool_option(TranscodeConfig::UPLOAD, "Transcode on upload: ", true);
|
||||
$sb->add_choice_option(TranscodeConfig::ENGINE, Media::IMAGE_MEDIA_ENGINES, "Engine", true);
|
||||
foreach (self::INPUT_FORMATS as $display=>$format) {
|
||||
if (in_array($format, MediaEngine::INPUT_SUPPORT[$engine])) {
|
||||
$outputs = $this->get_supported_output_formats($engine, $format);
|
||||
$sb->add_choice_option(TranscodeConfig::UPLOAD_PREFIX.$format, $outputs, "$display", 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);
|
||||
|
@ -98,22 +161,19 @@ class TranscodeImage extends Extension
|
|||
global $config;
|
||||
|
||||
if ($config->get_bool(TranscodeConfig::UPLOAD) == true) {
|
||||
$ext = strtolower($event->type);
|
||||
|
||||
$ext = Media::normalize_format($ext);
|
||||
|
||||
if ($event->type==EXTENSION_GIF&&Media::is_animated_gif($event->tmpname)) {
|
||||
$mime = strtolower($event->mime);
|
||||
if ($mime===MimeType::GIF&&MimeType::is_animated_gif($event->tmpname)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (in_array($ext, array_values(self::INPUT_FORMATS))) {
|
||||
$target_format = $config->get_string(TranscodeConfig::UPLOAD_PREFIX.$ext);
|
||||
if (empty($target_format)) {
|
||||
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, $ext, $target_format);
|
||||
$event->set_type(Media::determine_ext($target_format));
|
||||
$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());
|
||||
|
@ -142,9 +202,9 @@ class TranscodeImage extends Extension
|
|||
if (is_null($image_obj)) {
|
||||
$this->theme->display_error(404, "Image not found", "No image in the database has the ID #$image_id");
|
||||
} else {
|
||||
if (isset($_POST['transcode_format'])) {
|
||||
if (isset($_POST['transcode_mime'])) {
|
||||
try {
|
||||
$this->transcode_and_replace_image($image_obj, $_POST['transcode_format']);
|
||||
$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) {
|
||||
|
@ -163,7 +223,7 @@ class TranscodeImage extends Extension
|
|||
$engine = $config->get_string(TranscodeConfig::ENGINE);
|
||||
|
||||
if ($user->can(Permissions::EDIT_FILES)) {
|
||||
$event->add_action(self::ACTION_BULK_TRANSCODE, "Transcode", null, "", $this->theme->get_transcode_picker_html($this->get_supported_output_formats($engine)));
|
||||
$event->add_action(self::ACTION_BULK_TRANSCODE, "Transcode", null, "", $this->theme->get_transcode_picker_html($this->get_supported_output_mimes($engine)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,11 +233,11 @@ class TranscodeImage extends Extension
|
|||
|
||||
switch ($event->action) {
|
||||
case self::ACTION_BULK_TRANSCODE:
|
||||
if (!isset($_POST['transcode_format'])) {
|
||||
if (!isset($_POST['transcode_mime'])) {
|
||||
return;
|
||||
}
|
||||
if ($user->can(Permissions::EDIT_FILES)) {
|
||||
$format = $_POST['transcode_format'];
|
||||
$mime = $_POST['transcode_mime'];
|
||||
$total = 0;
|
||||
$size_difference = 0;
|
||||
foreach ($event->items as $image) {
|
||||
|
@ -186,7 +246,7 @@ class TranscodeImage extends Extension
|
|||
|
||||
$before_size = $image->filesize;
|
||||
|
||||
$new_image = $this->transcode_and_replace_image($image, $format);
|
||||
$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
|
||||
|
@ -194,7 +254,7 @@ class TranscodeImage extends Extension
|
|||
$total++;
|
||||
$size_difference += ($before_size - $new_image->filesize);
|
||||
} catch (Exception $e) {
|
||||
log_error("transcode", "Error while bulk transcode on item {$image->id} to $format: ".$e->getMessage());
|
||||
log_error("transcode", "Error while bulk transcode on item {$image->id} to $mime: ".$e->getMessage());
|
||||
try {
|
||||
$database->rollback();
|
||||
} catch (Exception $e) {
|
||||
|
@ -215,27 +275,24 @@ class TranscodeImage extends Extension
|
|||
}
|
||||
|
||||
|
||||
private function can_convert_format($engine, $format, ?bool $lossless = null): bool
|
||||
private function can_convert_mime($engine, $mime): bool
|
||||
{
|
||||
return Media::is_input_supported($engine, $format, $lossless);
|
||||
return MediaEngine::is_input_supported($engine, $mime);
|
||||
}
|
||||
|
||||
|
||||
private function get_supported_output_formats($engine, ?String $omit_format = null, ?bool $lossless = null): array
|
||||
private function get_supported_output_mimes($engine, ?String $omit_mime = null): array
|
||||
{
|
||||
if ($omit_format!=null) {
|
||||
$omit_format = Media::normalize_format($omit_format, $lossless);
|
||||
}
|
||||
$output = [];
|
||||
|
||||
|
||||
foreach (self::OUTPUT_FORMATS as $key=>$value) {
|
||||
foreach (self::OUTPUT_MIMES as $key=> $value) {
|
||||
if ($value=="") {
|
||||
$output[$key] = $value;
|
||||
continue;
|
||||
}
|
||||
if (Media::is_output_supported($engine, $value)
|
||||
&&(empty($omit_format)||$omit_format!=$value)) {
|
||||
if (MediaEngine::is_output_supported($engine, $value)
|
||||
&&(empty($omit_mime)||$omit_mime!=$value)) {
|
||||
$output[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
@ -244,11 +301,11 @@ class TranscodeImage extends Extension
|
|||
|
||||
|
||||
|
||||
private function transcode_and_replace_image(Image $image_obj, String $target_format): Image
|
||||
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->ext, $target_format);
|
||||
$tmp_filename = $this->transcode_image($original_file, $image_obj->get_mime(), $target_mime);
|
||||
|
||||
$new_image = new Image();
|
||||
$new_image->hash = md5_file($tmp_filename);
|
||||
|
@ -256,7 +313,6 @@ class TranscodeImage extends Extension
|
|||
$new_image->filename = $image_obj->filename;
|
||||
$new_image->width = $image_obj->width;
|
||||
$new_image->height = $image_obj->height;
|
||||
$new_image->ext = Media::determine_ext($target_format);
|
||||
|
||||
/* Move the new image into the main storage location */
|
||||
$target = warehouse_path(Image::IMAGE_DIR, $new_image->hash);
|
||||
|
@ -273,36 +329,36 @@ class TranscodeImage extends Extension
|
|||
}
|
||||
|
||||
|
||||
private function transcode_image(String $source_name, String $source_format, string $target_format): string
|
||||
private function transcode_image(String $source_name, String $source_mime, string $target_mime): string
|
||||
{
|
||||
global $config;
|
||||
|
||||
if ($source_format==$target_format) {
|
||||
throw new ImageTranscodeException("Source and target formats are the same: ".$source_format);
|
||||
if ($source_mime==$target_mime) {
|
||||
throw new ImageTranscodeException("Source and target MIMEs are the same: ".$source_mime);
|
||||
}
|
||||
|
||||
$engine = $config->get_string("transcode_engine");
|
||||
|
||||
|
||||
|
||||
if (!$this->can_convert_format($engine, $source_format)) {
|
||||
throw new ImageTranscodeException("Engine $engine does not support input format $source_format");
|
||||
if (!$this->can_convert_mime($engine, $source_mime)) {
|
||||
throw new ImageTranscodeException("Engine $engine does not support input MIME $source_mime");
|
||||
}
|
||||
if (!in_array($target_format, MediaEngine::OUTPUT_SUPPORT[$engine])) {
|
||||
throw new ImageTranscodeException("Engine $engine does not support output format $target_format");
|
||||
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_format, $target_format);
|
||||
return $this->transcode_image_gd($source_name, $source_mime, $target_mime);
|
||||
case "convert":
|
||||
return $this->transcode_image_convert($source_name, $source_format, $target_format);
|
||||
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_format, string $target_format): string
|
||||
private function transcode_image_gd(String $source_name, String $source_mime, string $target_mime): string
|
||||
{
|
||||
global $config;
|
||||
|
||||
|
@ -313,15 +369,14 @@ class TranscodeImage extends Extension
|
|||
$image = imagecreatefromstring(file_get_contents($source_name));
|
||||
try {
|
||||
$result = false;
|
||||
switch ($target_format) {
|
||||
case EXTENSION_WEBP:
|
||||
case Media::WEBP_LOSSY:
|
||||
switch ($target_mime) {
|
||||
case MimeType::WEBP:
|
||||
$result = imagewebp($image, $tmp_name, $q);
|
||||
break;
|
||||
case EXTENSION_PNG:
|
||||
case MimeType::PNG:
|
||||
$result = imagepng($image, $tmp_name, 9);
|
||||
break;
|
||||
case EXTENSION_JPG:
|
||||
case MimeType::JPEG:
|
||||
// In case of alpha channels
|
||||
$width = imagesx($image);
|
||||
$height = imagesy($image);
|
||||
|
@ -350,12 +405,12 @@ class TranscodeImage extends Extension
|
|||
imagedestroy($image);
|
||||
}
|
||||
if ($result===false) {
|
||||
throw new ImageTranscodeException("Error while transcoding ".$source_name." to ".$target_format);
|
||||
throw new ImageTranscodeException("Error while transcoding ".$source_name." to ".$target_mime);
|
||||
}
|
||||
return $tmp_name;
|
||||
}
|
||||
|
||||
private function transcode_image_convert(String $source_name, String $source_format, string $target_format): string
|
||||
private function transcode_image_convert(String $source_name, String $source_mime, string $target_mime): string
|
||||
{
|
||||
global $config;
|
||||
|
||||
|
@ -365,35 +420,35 @@ class TranscodeImage extends Extension
|
|||
if ($convert==null||$convert=="") {
|
||||
throw new ImageTranscodeException("ImageMagick path not configured");
|
||||
}
|
||||
$ext = Media::determine_ext($target_format);
|
||||
$ext = Media::determine_ext($target_mime);
|
||||
|
||||
$args = " -flatten ";
|
||||
$bg = "none";
|
||||
switch ($target_format) {
|
||||
case Media::WEBP_LOSSLESS:
|
||||
$args .= '-define webp:lossless=true';
|
||||
$args = " -flatten -background ";
|
||||
|
||||
if (Media::supports_alpha($target_mime)) {
|
||||
$args .= "none ";
|
||||
} else {
|
||||
$args .= "black ";
|
||||
}
|
||||
|
||||
switch ($target_mime) {
|
||||
case MimeType::PNG:
|
||||
$args .= ' -define png:compression-level=9';
|
||||
break;
|
||||
case Media::WEBP_LOSSY:
|
||||
$args .= '';
|
||||
break;
|
||||
case EXTENSION_PNG:
|
||||
$args .= '-define png:compression-level=9';
|
||||
case MimeType::WEBP_LOSSLESS:
|
||||
$args .= ' -define webp:lossless=true -quality 100 ';
|
||||
break;
|
||||
default:
|
||||
$bg = "black";
|
||||
$args .= ' -quality '.$q;
|
||||
break;
|
||||
}
|
||||
|
||||
$tmp_name = tempnam(sys_get_temp_dir(), "shimmie_transcode");
|
||||
|
||||
$source_type = "";
|
||||
switch ($source_format) {
|
||||
case EXTENSION_ICO:
|
||||
$source_type = "ico:";
|
||||
}
|
||||
$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);
|
||||
|
||||
$format = '"%s" %s -quality %u -background %s %s"%s" %s:"%s" 2>&1';
|
||||
$cmd = sprintf($format, $convert, $args, $q, $bg, $source_type, $source_name, $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);
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
function transcodeSubmit(e) {
|
||||
var format = document.getElementById('transcode_format').value;
|
||||
if(format!="webp-lossless" && format != "png") {
|
||||
var mime = document.getElementById('transcode_mime').value;
|
||||
if(!mime.includes("lossless=true") && format != "image/png") {
|
||||
var lossless = document.getElementById('image_lossless');
|
||||
if(lossless!=null && lossless.value=='1') {
|
||||
return confirm('You are about to transcode from a lossless format to a lossy format. Lossless formats compress with no quality loss, but converting to a lossy format always results in quality loss, and it will lose more quality every time it is done again on the same image. Are you sure you want to perform this transcode?');
|
||||
|
@ -8,4 +8,4 @@ function transcodeSubmit(e) {
|
|||
return confirm('Converting to a lossy format always results in quality loss, and it will lose more quality every time it is done again on the same image. Are you sure you want to perform this transcode?');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ class TranscodeImageTheme extends Themelet
|
|||
|
||||
public function get_transcode_picker_html(array $options)
|
||||
{
|
||||
$html = "<select id='transcode_format' name='transcode_format' required='required' >";
|
||||
$html = "<select id='transcode_mime' name='transcode_mime' required='required' >";
|
||||
foreach ($options as $display=>$value) {
|
||||
$html .= "<option value='$value'>$display</option>";
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ class Update extends Extension
|
|||
|
||||
log_info("update", "Attempting to download Shimmie commit: ".$commitSHA);
|
||||
if ($headers = transload($url, $filename)) {
|
||||
if (($headers['Content-Type'] !== MIME_TYPE_ZIP) || ((int) $headers['Content-Length'] !== filesize($filename))) {
|
||||
if (($headers['Content-Type'] !== MimeType::ZIP) || ((int) $headers['Content-Length'] !== filesize($filename))) {
|
||||
unlink("./data/update_{$commitSHA}.zip");
|
||||
log_warning("update", "Download failed: not zip / not same size as remote file.");
|
||||
return false;
|
||||
|
|
|
@ -186,6 +186,19 @@ class Upgrade extends Extension
|
|||
$database->execute($database->scoreql_to_sql("UPDATE images SET lossless = SCORE_BOOL_N, video = SCORE_BOOL_Y WHERE ext IN ('flv','mp4','m4v','ogv','webm')"));
|
||||
$this->set_version("db_version", 18);
|
||||
}
|
||||
|
||||
if ($this->get_version("db_version") < 19) {
|
||||
log_info("upgrade", "Adding MIME type column");
|
||||
|
||||
$database->execute($database->scoreql_to_sql(
|
||||
"ALTER TABLE images ADD COLUMN mime varchar(512) NULL"
|
||||
));
|
||||
// Column is primed in mime extension
|
||||
log_info("upgrade", "Setting index for mime column");
|
||||
$database->execute('CREATE INDEX images_mime_idx ON images(mime)');
|
||||
|
||||
$this->set_version("db_version", 19);
|
||||
}
|
||||
}
|
||||
|
||||
public function get_priority(): int
|
||||
|
|
|
@ -12,7 +12,7 @@ class DataUploadEvent extends Event
|
|||
/** @var string */
|
||||
public $hash;
|
||||
/** @var string */
|
||||
public $type = "";
|
||||
public $mime = "";
|
||||
/** @var int */
|
||||
public $image_id = -1;
|
||||
/** @var int */
|
||||
|
@ -44,29 +44,23 @@ class DataUploadEvent extends Event
|
|||
|
||||
$this->set_tmpname($tmpname);
|
||||
|
||||
if ($config->get_bool("upload_use_mime")) {
|
||||
$filetype = get_extension_for_file($tmpname);
|
||||
if (array_key_exists("extension", $metadata)) {
|
||||
$mime = MimeType::get_for_file($tmpname, $metadata["extension"]);
|
||||
} else {
|
||||
$mime = MimeType::get_for_file($tmpname);
|
||||
}
|
||||
|
||||
if (empty($filetype)) {
|
||||
if (array_key_exists('extension', $metadata) && !empty($metadata['extension'])) {
|
||||
$filetype = strtolower($metadata['extension']);
|
||||
} else {
|
||||
throw new UploadException("Could not determine extension for file " . $metadata["filename"]);
|
||||
}
|
||||
if (empty($mime)) {
|
||||
throw new UploadException("Could not determine mime type for file " . $metadata["filename"]);
|
||||
}
|
||||
|
||||
if (empty($filetype)) {
|
||||
throw new UploadException("Could not determine extension for file " . $metadata["filename"]);
|
||||
}
|
||||
|
||||
$this->set_type($filetype);
|
||||
$this->set_mime($mime);
|
||||
}
|
||||
|
||||
public function set_type(String $type)
|
||||
public function set_mime(String $mime)
|
||||
{
|
||||
$this->type = strtolower($type);
|
||||
$this->metadata["extension"] = $this->type;
|
||||
$this->mime = strtolower($mime);
|
||||
$this->metadata["mime"] = $this->mime;
|
||||
}
|
||||
|
||||
public function set_tmpname(String $tmpname)
|
||||
|
@ -111,7 +105,6 @@ class Upload extends Extension
|
|||
$config->set_default_int('upload_size', parse_shorthand_int('1MB'));
|
||||
$config->set_default_int('upload_min_free_space', parse_shorthand_int('100MB'));
|
||||
$config->set_default_bool('upload_tlsource', true);
|
||||
$config->set_default_bool('upload_use_mime', false);
|
||||
|
||||
$this->is_full = false;
|
||||
|
||||
|
@ -147,7 +140,6 @@ class Upload extends Extension
|
|||
$sb->add_label("<i>PHP Limit = " . ini_get('upload_max_filesize') . "</i>");
|
||||
$sb->add_choice_option("transload_engine", $tes, "<br/>Transload: ");
|
||||
$sb->add_bool_option("upload_tlsource", "<br/>Use transloaded URL as source if none is provided: ");
|
||||
$sb->add_bool_option("upload_use_mime", "<br/>Use mime type to determine file types: ");
|
||||
$event->panel->add_block($sb);
|
||||
}
|
||||
|
||||
|
@ -347,9 +339,15 @@ class Upload extends Extension
|
|||
$pathinfo = pathinfo($file['name']);
|
||||
$metadata = [];
|
||||
$metadata['filename'] = $pathinfo['basename'];
|
||||
$metadata['extension'] = "";
|
||||
if (array_key_exists('extension', $pathinfo)) {
|
||||
$metadata['extension'] = $pathinfo['extension'];
|
||||
}
|
||||
$metadata['mime'] = MimeType::get_for_file($file['tmp_name'], $metadata['extension']);
|
||||
if (empty($metadata['mime'])) {
|
||||
throw new UploadException("Unable to determine MIME for file ".$metadata['filename']);
|
||||
}
|
||||
|
||||
$metadata['tags'] = $tags;
|
||||
$metadata['source'] = $source;
|
||||
|
||||
|
@ -357,7 +355,7 @@ class Upload extends Extension
|
|||
$event->replace_id = $replace_id;
|
||||
send_event($event);
|
||||
if ($event->image_id == -1) {
|
||||
throw new UploadException("File type not supported: " . $metadata['extension']);
|
||||
throw new UploadException("MIME type not supported: " . $metadata['mime']);
|
||||
}
|
||||
$page->add_http_header("X-Shimmie-Image-ID: " . $event->image_id);
|
||||
} catch (UploadException $ex) {
|
||||
|
@ -425,15 +423,6 @@ class Upload extends Extension
|
|||
$metadata['tags'] = $tags;
|
||||
$metadata['source'] = (($url == $source) && !$config->get_bool('upload_tlsource') ? "" : $source);
|
||||
|
||||
$ext = false;
|
||||
if (is_array($headers)) {
|
||||
$ext = get_extension(findHeader($headers, 'Content-Type'));
|
||||
}
|
||||
if ($ext === false) {
|
||||
$ext = $pathinfo['extension'];
|
||||
}
|
||||
$metadata['extension'] = $ext;
|
||||
|
||||
/* check for locked > adds to metadata if it has */
|
||||
if (!empty($locked)) {
|
||||
$metadata['locked'] = $locked ? "on" : "";
|
||||
|
|
|
@ -242,8 +242,8 @@ class UploadTheme extends Themelet
|
|||
";
|
||||
}
|
||||
|
||||
protected function get_accept()
|
||||
protected function get_accept(): string
|
||||
{
|
||||
return join(",", DataHandlerExtension::get_all_supported_exts());
|
||||
return ".".join(",.", DataHandlerExtension::get_all_supported_exts());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ class CustomViewImageTheme extends ViewImageTheme
|
|||
$h_owner = html_escape($image->get_owner()->name);
|
||||
$h_ownerlink = "<a href='".make_link("user/$h_owner")."'>$h_owner</a>";
|
||||
$h_ip = html_escape($image->owner_ip);
|
||||
$h_type = html_escape($image->get_mime_type());
|
||||
$h_type = html_escape($image->get_mime());
|
||||
$h_date = autodate($image->posted);
|
||||
$h_filesize = to_shorthand_int($image->filesize);
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ class CustomViewImageTheme extends ViewImageTheme
|
|||
$h_owner = html_escape($image->get_owner()->name);
|
||||
$h_ownerlink = "<a href='".make_link("user/$h_owner")."'>$h_owner</a>";
|
||||
$h_ip = html_escape($image->owner_ip);
|
||||
$h_type = html_escape($image->get_mime_type());
|
||||
$h_type = html_escape($image->get_mime());
|
||||
$h_date = autodate($image->posted);
|
||||
$h_filesize = to_shorthand_int($image->filesize);
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ class CustomViewImageTheme extends ViewImageTheme
|
|||
$h_owner = html_escape($image->get_owner()->name);
|
||||
$h_ownerlink = "<a href='".make_link("user/$h_owner")."'>$h_owner</a>";
|
||||
$h_ip = html_escape($image->owner_ip);
|
||||
$h_type = html_escape($image->get_mime_type());
|
||||
$h_type = html_escape($image->get_mime());
|
||||
$h_date = autodate($image->posted);
|
||||
$h_filesize = to_shorthand_int($image->filesize);
|
||||
|
||||
|
|
|
@ -16,10 +16,10 @@ class Themelet extends BaseThemelet
|
|||
$h_thumb_link = $image->get_thumb_link();
|
||||
$h_tip = html_escape($image->get_tooltip());
|
||||
$h_tags = strtolower($image->get_tag_list());
|
||||
$h_ext = strtolower($image->ext);
|
||||
$h_ext = strtolower($image->get_ext());
|
||||
|
||||
// If file is flash or svg then sets thumbnail to max size.
|
||||
if ($image->ext === EXTENSION_FLASH || $image->ext === EXTENSION_SVG) {
|
||||
if ($image->get_mime() === MimeType::FLASH || $image->get_mime() === MimeType::SVG) {
|
||||
$tsize = get_thumbnail_size($config->get_int('thumb_width'), $config->get_int('thumb_height'));
|
||||
} else {
|
||||
$tsize = get_thumbnail_size($image->width, $image->height);
|
||||
|
|
Reference in a new issue