Media.php 6.7 KB

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