238 lines
7.6 KiB
PHP
238 lines
7.6 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @package Grav\Common\Page
|
|
*
|
|
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
|
|
* @license MIT License; see LICENSE file for details.
|
|
*/
|
|
|
|
namespace Grav\Common\Page;
|
|
|
|
use FilesystemIterator;
|
|
use Grav\Common\Grav;
|
|
use Grav\Common\Media\Interfaces\MediaObjectInterface;
|
|
use Grav\Common\Yaml;
|
|
use Grav\Common\Page\Medium\AbstractMedia;
|
|
use Grav\Common\Page\Medium\GlobalMedia;
|
|
use Grav\Common\Page\Medium\MediumFactory;
|
|
use RocketTheme\Toolbox\File\File;
|
|
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
|
use function in_array;
|
|
|
|
/**
|
|
* Class Media
|
|
* @package Grav\Common\Page
|
|
*/
|
|
class Media extends AbstractMedia
|
|
{
|
|
/** @var GlobalMedia */
|
|
protected static $global;
|
|
|
|
/** @var array */
|
|
protected $standard_exif = ['FileSize', 'MimeType', 'height', 'width'];
|
|
|
|
/**
|
|
* @param string $path
|
|
* @param array|null $media_order
|
|
* @param bool $load
|
|
*/
|
|
public function __construct($path, array $media_order = null, $load = true)
|
|
{
|
|
$this->setPath($path);
|
|
$this->media_order = $media_order;
|
|
|
|
$this->__wakeup();
|
|
if ($load) {
|
|
$this->init();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize static variables on unserialize.
|
|
*/
|
|
public function __wakeup()
|
|
{
|
|
if (null === static::$global) {
|
|
// Add fallback to global media.
|
|
static::$global = GlobalMedia::getInstance();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string $offset
|
|
* @return bool
|
|
*/
|
|
public function offsetExists($offset)
|
|
{
|
|
return parent::offsetExists($offset) ?: isset(static::$global[$offset]);
|
|
}
|
|
|
|
/**
|
|
* @param string $offset
|
|
* @return MediaObjectInterface|null
|
|
*/
|
|
public function offsetGet($offset)
|
|
{
|
|
return parent::offsetGet($offset) ?: static::$global[$offset];
|
|
}
|
|
|
|
/**
|
|
* Initialize class.
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function init()
|
|
{
|
|
/** @var UniformResourceLocator $locator */
|
|
$locator = Grav::instance()['locator'];
|
|
$config = Grav::instance()['config'];
|
|
$exif_reader = isset(Grav::instance()['exif']) ? Grav::instance()['exif']->getReader() : false;
|
|
$media_types = array_keys(Grav::instance()['config']->get('media.types'));
|
|
$path = $this->getPath();
|
|
|
|
// Handle special cases where page doesn't exist in filesystem.
|
|
if (!$path || !is_dir($path)) {
|
|
return;
|
|
}
|
|
|
|
$iterator = new FilesystemIterator($path, FilesystemIterator::UNIX_PATHS | FilesystemIterator::SKIP_DOTS);
|
|
|
|
$media = [];
|
|
|
|
foreach ($iterator as $file => $info) {
|
|
// Ignore folders and Markdown files.
|
|
$filename = $info->getFilename();
|
|
if (!$info->isFile() || $info->getExtension() === 'md' || $filename === 'frontmatter.yaml' || strpos($filename, '.') === 0) {
|
|
continue;
|
|
}
|
|
|
|
// Find out what type we're dealing with
|
|
[$basename, $ext, $type, $extra] = $this->getFileParts($filename);
|
|
|
|
if (!in_array(strtolower($ext), $media_types, true)) {
|
|
continue;
|
|
}
|
|
|
|
if ($type === 'alternative') {
|
|
$media["{$basename}.{$ext}"][$type][$extra] = ['file' => $file, 'size' => $info->getSize()];
|
|
} else {
|
|
$media["{$basename}.{$ext}"][$type] = ['file' => $file, 'size' => $info->getSize()];
|
|
}
|
|
}
|
|
|
|
foreach ($media as $name => $types) {
|
|
// First prepare the alternatives in case there is no base medium
|
|
if (!empty($types['alternative'])) {
|
|
/**
|
|
* @var string|int $ratio
|
|
* @var array $alt
|
|
*/
|
|
foreach ($types['alternative'] as $ratio => &$alt) {
|
|
$alt['file'] = $this->createFromFile($alt['file']);
|
|
|
|
if (empty($alt['file'])) {
|
|
unset($types['alternative'][$ratio]);
|
|
} else {
|
|
$alt['file']->set('size', $alt['size']);
|
|
}
|
|
}
|
|
unset($alt);
|
|
}
|
|
|
|
$file_path = null;
|
|
|
|
// Create the base medium
|
|
if (empty($types['base'])) {
|
|
if (!isset($types['alternative'])) {
|
|
continue;
|
|
}
|
|
|
|
$max = max(array_keys($types['alternative']));
|
|
$medium = $types['alternative'][$max]['file'];
|
|
$file_path = $medium->path();
|
|
$medium = MediumFactory::scaledFromMedium($medium, $max, 1)['file'];
|
|
} else {
|
|
$medium = $this->createFromFile($types['base']['file']);
|
|
if ($medium) {
|
|
$medium->set('size', $types['base']['size']);
|
|
$file_path = $medium->path();
|
|
}
|
|
}
|
|
|
|
if (empty($medium)) {
|
|
continue;
|
|
}
|
|
|
|
// metadata file
|
|
$meta_path = $file_path . '.meta.yaml';
|
|
|
|
if (file_exists($meta_path)) {
|
|
$types['meta']['file'] = $meta_path;
|
|
} elseif ($file_path && $exif_reader && $medium->get('mime') === 'image/jpeg' && empty($types['meta']) && $config->get('system.media.auto_metadata_exif')) {
|
|
$meta = $exif_reader->read($file_path);
|
|
|
|
if ($meta) {
|
|
$meta_data = $meta->getData();
|
|
$meta_trimmed = array_diff_key($meta_data, array_flip($this->standard_exif));
|
|
if ($meta_trimmed) {
|
|
if ($locator->isStream($meta_path)) {
|
|
$file = File::instance($locator->findResource($meta_path, true, true));
|
|
} else {
|
|
$file = File::instance($meta_path);
|
|
}
|
|
$file->save(Yaml::dump($meta_trimmed));
|
|
$types['meta']['file'] = $meta_path;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!empty($types['meta'])) {
|
|
$medium->addMetaFile($types['meta']['file']);
|
|
}
|
|
|
|
if (!empty($types['thumb'])) {
|
|
// We will not turn it into medium yet because user might never request the thumbnail
|
|
// not wasting any resources on that, maybe we should do this for medium in general?
|
|
$medium->set('thumbnails.page', $types['thumb']['file']);
|
|
}
|
|
|
|
// Build missing alternatives
|
|
if (!empty($types['alternative'])) {
|
|
$alternatives = $types['alternative'];
|
|
$max = max(array_keys($alternatives));
|
|
|
|
for ($i=$max; $i > 1; $i--) {
|
|
if (isset($alternatives[$i])) {
|
|
continue;
|
|
}
|
|
|
|
$types['alternative'][$i] = MediumFactory::scaledFromMedium($alternatives[$max]['file'], $max, $i);
|
|
}
|
|
|
|
foreach ($types['alternative'] as $altMedium) {
|
|
if ($altMedium['file'] != $medium) {
|
|
$altWidth = $altMedium['file']->get('width');
|
|
$medWidth = $medium->get('width');
|
|
if ($altWidth && $medWidth) {
|
|
$ratio = $altWidth / $medWidth;
|
|
$medium->addAlternative($ratio, $altMedium['file']);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->add($name, $medium);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return string|null
|
|
* @deprecated 1.6 Use $this->getPath() instead.
|
|
*/
|
|
public function path(): ?string
|
|
{
|
|
return $this->getPath();
|
|
}
|
|
}
|