Media.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. <?php
  2. /**
  3. * @package Grav\Common\Page
  4. *
  5. * @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
  6. * @license MIT License; see LICENSE file for details.
  7. */
  8. namespace Grav\Common\Page;
  9. use Grav\Common\Grav;
  10. use Grav\Common\Yaml;
  11. use Grav\Common\Page\Medium\AbstractMedia;
  12. use Grav\Common\Page\Medium\GlobalMedia;
  13. use Grav\Common\Page\Medium\MediumFactory;
  14. use RocketTheme\Toolbox\File\File;
  15. use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
  16. class Media extends AbstractMedia
  17. {
  18. protected static $global;
  19. protected $standard_exif = ['FileSize', 'MimeType', 'height', 'width'];
  20. /**
  21. * @param string $path
  22. * @param array $media_order
  23. * @param bool $load
  24. */
  25. public function __construct($path, array $media_order = null, $load = true)
  26. {
  27. $this->setPath($path);
  28. $this->media_order = $media_order;
  29. $this->__wakeup();
  30. if ($load) {
  31. $this->init();
  32. }
  33. }
  34. /**
  35. * Initialize static variables on unserialize.
  36. */
  37. public function __wakeup()
  38. {
  39. if (!isset(static::$global)) {
  40. // Add fallback to global media.
  41. static::$global = new GlobalMedia();
  42. }
  43. }
  44. /**
  45. * @param mixed $offset
  46. *
  47. * @return bool
  48. */
  49. public function offsetExists($offset)
  50. {
  51. return parent::offsetExists($offset) ?: isset(static::$global[$offset]);
  52. }
  53. /**
  54. * @param mixed $offset
  55. *
  56. * @return mixed
  57. */
  58. public function offsetGet($offset)
  59. {
  60. return parent::offsetGet($offset) ?: static::$global[$offset];
  61. }
  62. /**
  63. * Initialize class.
  64. */
  65. protected function init()
  66. {
  67. /** @var UniformResourceLocator $locator */
  68. $locator = Grav::instance()['locator'];
  69. $config = Grav::instance()['config'];
  70. $locator = Grav::instance()['locator'];
  71. $exif_reader = isset(Grav::instance()['exif']) ? Grav::instance()['exif']->getReader() : false;
  72. $media_types = array_keys(Grav::instance()['config']->get('media.types'));
  73. // Handle special cases where page doesn't exist in filesystem.
  74. if (!is_dir($this->getPath())) {
  75. return;
  76. }
  77. $iterator = new \FilesystemIterator($this->getPath(), \FilesystemIterator::UNIX_PATHS | \FilesystemIterator::SKIP_DOTS);
  78. $media = [];
  79. /** @var \DirectoryIterator $info */
  80. foreach ($iterator as $path => $info) {
  81. // Ignore folders and Markdown files.
  82. if (!$info->isFile() || $info->getExtension() === 'md' || strpos($info->getFilename(), '.') === 0) {
  83. continue;
  84. }
  85. // Find out what type we're dealing with
  86. list($basename, $ext, $type, $extra) = $this->getFileParts($info->getFilename());
  87. if (!\in_array(strtolower($ext), $media_types, true)) {
  88. continue;
  89. }
  90. if ($type === 'alternative') {
  91. $media["{$basename}.{$ext}"][$type][$extra] = ['file' => $path, 'size' => $info->getSize()];
  92. } else {
  93. $media["{$basename}.{$ext}"][$type] = ['file' => $path, 'size' => $info->getSize()];
  94. }
  95. }
  96. foreach ($media as $name => $types) {
  97. // First prepare the alternatives in case there is no base medium
  98. if (!empty($types['alternative'])) {
  99. foreach ($types['alternative'] as $ratio => &$alt) {
  100. $alt['file'] = MediumFactory::fromFile($alt['file']);
  101. if (!$alt['file']) {
  102. unset($types['alternative'][$ratio]);
  103. } else {
  104. $alt['file']->set('size', $alt['size']);
  105. }
  106. }
  107. }
  108. $file_path = null;
  109. // Create the base medium
  110. if (empty($types['base'])) {
  111. if (!isset($types['alternative'])) {
  112. continue;
  113. }
  114. $max = max(array_keys($types['alternative']));
  115. $medium = $types['alternative'][$max]['file'];
  116. $file_path = $medium->path();
  117. $medium = MediumFactory::scaledFromMedium($medium, $max, 1)['file'];
  118. } else {
  119. $medium = MediumFactory::fromFile($types['base']['file']);
  120. $medium && $medium->set('size', $types['base']['size']);
  121. $file_path = $medium->path();
  122. }
  123. if (empty($medium)) {
  124. continue;
  125. }
  126. // metadata file
  127. $meta_path = $file_path . '.meta.yaml';
  128. if (file_exists($meta_path)) {
  129. $types['meta']['file'] = $meta_path;
  130. } elseif ($file_path && $exif_reader && $medium->get('mime') === 'image/jpeg' && empty($types['meta']) && $config->get('system.media.auto_metadata_exif')) {
  131. $meta = $exif_reader->read($file_path);
  132. if ($meta) {
  133. $meta_data = $meta->getData();
  134. $meta_trimmed = array_diff_key($meta_data, array_flip($this->standard_exif));
  135. if ($meta_trimmed) {
  136. if ($locator->isStream($meta_path)) {
  137. $file = File::instance($locator->findResource($meta_path, true, true));
  138. } else {
  139. $file = File::instance($meta_path);
  140. }
  141. $file->save(Yaml::dump($meta_trimmed));
  142. $types['meta']['file'] = $meta_path;
  143. }
  144. }
  145. }
  146. if (!empty($types['meta'])) {
  147. $medium->addMetaFile($types['meta']['file']);
  148. }
  149. if (!empty($types['thumb'])) {
  150. // We will not turn it into medium yet because user might never request the thumbnail
  151. // not wasting any resources on that, maybe we should do this for medium in general?
  152. $medium->set('thumbnails.page', $types['thumb']['file']);
  153. }
  154. // Build missing alternatives
  155. if (!empty($types['alternative'])) {
  156. $alternatives = $types['alternative'];
  157. $max = max(array_keys($alternatives));
  158. for ($i=$max; $i > 1; $i--) {
  159. if (isset($alternatives[$i])) {
  160. continue;
  161. }
  162. $types['alternative'][$i] = MediumFactory::scaledFromMedium($alternatives[$max]['file'], $max, $i);
  163. }
  164. foreach ($types['alternative'] as $altMedium) {
  165. if ($altMedium['file'] != $medium) {
  166. $altWidth = $altMedium['file']->get('width');
  167. $medWidth = $medium->get('width');
  168. if ($altWidth && $medWidth) {
  169. $ratio = $altWidth / $medWidth;
  170. $medium->addAlternative($ratio, $altMedium['file']);
  171. }
  172. }
  173. }
  174. }
  175. $this->add($name, $medium);
  176. }
  177. }
  178. /**
  179. * @return string
  180. * @deprecated 1.6 Use $this->getPath() instead.
  181. */
  182. public function path()
  183. {
  184. return $this->getPath();
  185. }
  186. }