diff --git a/classes/Storage/StorageGlobals.php b/classes/Storage/StorageGlobals.php index dc292062..aa92aa4c 100755 --- a/classes/Storage/StorageGlobals.php +++ b/classes/Storage/StorageGlobals.php @@ -88,6 +88,9 @@ final class StorageGlobals { /** @var bool */ private $deleteFromStorage = false; + /** @var bool */ + private $cacheLookups = true; + /** @var array */ private $alternateFormatTypes = ['image/pdf', 'application/pdf', 'image/psd', 'application/vnd.adobe.illustrator']; @@ -120,6 +123,8 @@ private function __construct() { $this->privacyVideo = Environment::Option('mcloud-storage-privacy-video', null, "inherit"); $this->privacyDocs = Environment::Option('mcloud-storage-privacy-docs', null, "inherit"); + $this->cacheLookups = Environment::Option('mcloud-storage-cache-lookups', null, true); + $ignored = Environment::Option('mcloud-storage-ignored-mime-types', null, ''); $ignored_lines = explode("\n", $ignored); if(count($ignored_lines) <= 1) { @@ -354,6 +359,11 @@ public static function deleteFromStorage() { return self::instance()->deleteFromStorage; } + /** @return bool */ + public static function cacheLookups() { + return self::instance()->cacheLookups; + } + /** * @return array */ diff --git a/classes/Storage/StoragePostMap.php b/classes/Storage/StoragePostMap.php new file mode 100755 index 00000000..c594b662 --- /dev/null +++ b/classes/Storage/StoragePostMap.php @@ -0,0 +1,148 @@ +base_prefix.'mcloud_post_map'; + $exists = ($wpdb->get_var("SHOW TABLES LIKE '$tableName'") == $tableName); + if ($exists) { + static::$installed = true; + return true; + } + } + + return static::installMapTable(); + } + + protected static function installMapTable() { + global $wpdb; + + $tableName = $wpdb->base_prefix.'mcloud_post_map'; + $charset = $wpdb->get_charset_collate(); + + $sql = "CREATE TABLE {$tableName} ( + id BIGINT AUTO_INCREMENT, + post_id BIGINT NOT NULL, + post_url VARCHAR(255) NOT NULL, + PRIMARY KEY (id), + KEY post_id(post_id), + KEY post_url(post_url(255)) +) {$charset};"; + + require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); + dbDelta($sql); + + $exists = ($wpdb->get_var("SHOW TABLES LIKE '$tableName'") == $tableName); + if ($exists) { + update_site_option(self::DB_KEY, self::DB_VERSION); + static::$installed = true; + return true; + } + + static::$installed = false; + return false; + } + + //endregion + + //region Queries + public static function uncachedAttachmentIdFromURL($url, $bucketName) { + global $wpdb; + + $query = $wpdb->prepare("select ID from {$wpdb->posts} where post_type='attachment' and guid = %s order by ID desc limit 1", $url); + $postId = $wpdb->get_var($query); + + if (empty($postId)) { + $parsedUrl = parse_url($url); + $path = ltrim($parsedUrl['path'], '/'); + + if (!empty($bucketName) && (strpos($path, $bucketName) === 0)) { + $path = ltrim(str_replace($bucketName,'', $path),'/'); + } + + $path = apply_filters('media-cloud/glide/clean-path', $path); + + $query = $wpdb->prepare("select ID from {$wpdb->posts} where post_type='attachment' and guid like %s order by ID desc limit 1", '%'.$path); + $postId = $wpdb->get_var($query); + } + + return $postId; + } + + public static function attachmentIdFromURL($postId, $url, $bucketName) { + if (!empty($postId)) { + return $postId; + } + + if (isset(static::$cache[$url])) { + return static::$cache[$url]; + } + + if (!empty(StorageGlobals::cacheLookups())) { + global $wpdb; + + $tableName = $wpdb->base_prefix.'mcloud_post_map'; + + if (static::verifyInstalled()) { + $query = $wpdb->prepare("select post_id from {$tableName} where post_url = %s order by post_id desc limit 1", $url); + $postId = (int)$wpdb->get_var($query); + if (($postId === -1) || !empty($postId)) { + return ($postId === -1) ? null : $postId; + } + } + + $postId = static::uncachedAttachmentIdFromURL($url, $bucketName); + + if (!empty($postId)) { + static::$cache[$url] = $postId; + if (static::$installed === true) { + $wpdb->insert($tableName, ['post_id' => empty($postId) ? -1 : $postId, 'post_url' => $url], ['%d', '%s']); + } + } + + return $postId; + } + + $postId = static::uncachedAttachmentIdFromURL($url, $bucketName); + return $postId; + } + //endregion +} \ No newline at end of file diff --git a/classes/Tools/Storage/StorageTool.php b/classes/Tools/Storage/StorageTool.php index 87224ffc..51c48e28 100755 --- a/classes/Tools/Storage/StorageTool.php +++ b/classes/Tools/Storage/StorageTool.php @@ -19,6 +19,7 @@ use ILAB\MediaCloud\Storage\StorageInterface ; use ILAB\MediaCloud\Storage\StorageManager ; use ILAB\MediaCloud\Storage\StorageGlobals ; +use ILAB\MediaCloud\Storage\StoragePostMap ; use ILAB\MediaCloud\Tasks\TaskManager ; use ILAB\MediaCloud\Tasks\TaskSchedule ; use ILAB\MediaCloud\Tools\Storage\Tasks\CleanUploadsTask ; @@ -251,6 +252,21 @@ public function setup() TaskManager::registerTask( UnlinkTask::class ); if ( $this->enabled() ) { + if ( is_admin() ) { + + if ( empty(get_option( 'uploads_use_yearmonth_folders' )) && empty(StorageGlobals::prefixFormat()) ) { + $mediaUrl = ilab_admin_url( 'options-media.php' ); + $settingsUrl = ilab_admin_url( 'admin.php?page=media-cloud-settings-storage#upload-handling' ); + NoticeManager::instance()->displayAdminNotice( + 'warning', + "You have the WordPress setting Organize my uploads into month and year based folders disabled, but haven't specified an Upload Path in Cloud Storage Settings. It is recommended that you either enable that setting, or set an upload directory. We recommend setting the Upload Path to @{date:Y/m}.", + true, + 'mcloud-no-upload-path', + 365 + ); + } + + } TaskManager::registerTask( CleanUploadsTask::class ); TaskManager::registerTask( DeleteUploadsTask::class ); foreach ( $this->toolInfo['compatibleImageOptimizers'] as $key => $plugin ) { @@ -1267,6 +1283,9 @@ private function fixOffloadS3Meta( $postId, $meta ) public function addAttachment( $post_id ) { $file = get_post_meta( $post_id, '_wp_attached_file', true ); + if ( !isset( $this->uploadedDocs[$file] ) ) { + $file = '/' . $file; + } if ( isset( $this->uploadedDocs[$file] ) ) { add_post_meta( $post_id, 'ilab_s3_info', $this->uploadedDocs[$file] ); @@ -1276,6 +1295,11 @@ public function addAttachment( $post_id ) $file, $this->uploadedDocs[$file] ); + } else { + Logger::info( "addAttachment - Missing '{$file}' key on uploadeDocs." ); + $keys = array_keys( $this->uploadedDocs ); + $keyList = implode( ' , ', $keys ); + Logger::info( 'addAttachment - Have keys: ' . $keyList ); } } @@ -2588,19 +2612,7 @@ private function replaceImageInContent( $id, $data, $content ) public function attachmentIdFromURL( $postId, $url ) { - if ( !empty($postId) ) { - return $postId; - } - global $wpdb ; - $parsedUrl = parse_url( $url ); - $path = ltrim( $parsedUrl['path'], '/' ); - if ( strpos( $path, $this->client()->bucket() ) === 0 ) { - $path = ltrim( str_replace( $this->client()->bucket(), '', $path ), '/' ); - } - $path = apply_filters( 'media-cloud/glide/clean-path', $path ); - $query = $wpdb->prepare( "select ID from {$wpdb->posts} where post_type='attachment' and guid like %s order by ID desc limit 1", '%' . $path ); - $postId = $wpdb->get_var( $query ); - return $postId; + return StoragePostMap::attachmentIdFromURL( $postId, $url, $this->client()->bucket() ); } //endregion diff --git a/classes/Utilities/Misc/Symfony/Component/Config/ConfigCache.php b/classes/Utilities/Misc/Symfony/Component/Config/ConfigCache.php new file mode 100755 index 00000000..beea9642 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/ConfigCache.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\SelfCheckingResourceChecker; +/** + * ConfigCache caches arbitrary content in files on disk. + * + * When in debug mode, those metadata resources that implement + * \Symfony\Component\Config\Resource\SelfCheckingResourceInterface will + * be used to check cache freshness. + * + * @author Fabien Potencier + * @author Matthias Pigulla + */ +class ConfigCache extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\ResourceCheckerConfigCache +{ + private $debug; + /** + * @param string $file The absolute cache path + * @param bool $debug Whether debugging is enabled or not + */ + public function __construct($file, $debug) + { + $this->debug = (bool) $debug; + $checkers = []; + if (\true === $this->debug) { + $checkers = [new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\SelfCheckingResourceChecker()]; + } + parent::__construct($file, $checkers); + } + /** + * Checks if the cache is still fresh. + * + * This implementation always returns true when debug is off and the + * cache file exists. + * + * @return bool true if the cache is fresh, false otherwise + */ + public function isFresh() + { + if (!$this->debug && \is_file($this->getPath())) { + return \true; + } + return parent::isFresh(); + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/ConfigCacheFactory.php b/classes/Utilities/Misc/Symfony/Component/Config/ConfigCacheFactory.php new file mode 100755 index 00000000..e887294b --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/ConfigCacheFactory.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config; + +/** + * Basic implementation of ConfigCacheFactoryInterface that + * creates an instance of the default ConfigCache. + * + * This factory and/or cache do not support cache validation + * by means of ResourceChecker instances (that is, service-based). + * + * @author Matthias Pigulla + */ +class ConfigCacheFactory implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\ConfigCacheFactoryInterface +{ + private $debug; + /** + * @param bool $debug The debug flag to pass to ConfigCache + */ + public function __construct($debug) + { + $this->debug = $debug; + } + /** + * {@inheritdoc} + */ + public function cache($file, $callback) + { + if (!\is_callable($callback)) { + throw new \InvalidArgumentException(\sprintf('Invalid type for callback argument. Expected callable, but got "%s".', \gettype($callback))); + } + $cache = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\ConfigCache($file, $this->debug); + if (!$cache->isFresh()) { + \call_user_func($callback, $cache); + } + return $cache; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/ConfigCacheFactoryInterface.php b/classes/Utilities/Misc/Symfony/Component/Config/ConfigCacheFactoryInterface.php new file mode 100755 index 00000000..bdf95bd9 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/ConfigCacheFactoryInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config; + +/** + * Interface for a ConfigCache factory. This factory creates + * an instance of ConfigCacheInterface and initializes the + * cache if necessary. + * + * @author Matthias Pigulla + */ +interface ConfigCacheFactoryInterface +{ + /** + * Creates a cache instance and (re-)initializes it if necessary. + * + * @param string $file The absolute cache file path + * @param callable $callable The callable to be executed when the cache needs to be filled (i. e. is not fresh). The cache will be passed as the only parameter to this callback + * + * @return ConfigCacheInterface The cache instance + */ + public function cache($file, $callable); +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/ConfigCacheInterface.php b/classes/Utilities/Misc/Symfony/Component/Config/ConfigCacheInterface.php new file mode 100755 index 00000000..37c98f5f --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/ConfigCacheInterface.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\ResourceInterface; +/** + * Interface for ConfigCache. + * + * @author Matthias Pigulla + */ +interface ConfigCacheInterface +{ + /** + * Gets the cache file path. + * + * @return string The cache file path + */ + public function getPath(); + /** + * Checks if the cache is still fresh. + * + * This check should take the metadata passed to the write() method into consideration. + * + * @return bool Whether the cache is still fresh + */ + public function isFresh(); + /** + * Writes the given content into the cache file. Metadata will be stored + * independently and can be used to check cache freshness at a later time. + * + * @param string $content The content to write into the cache + * @param ResourceInterface[]|null $metadata An array of ResourceInterface instances + * + * @throws \RuntimeException When the cache file cannot be written + */ + public function write($content, array $metadata = null); +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/ArrayNode.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/ArrayNode.php new file mode 100755 index 00000000..1f2d3ff7 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/ArrayNode.php @@ -0,0 +1,330 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidTypeException; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\UnsetKeyException; +/** + * Represents an Array node in the config tree. + * + * @author Johannes M. Schmitt + */ +class ArrayNode extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\BaseNode implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\PrototypeNodeInterface +{ + protected $xmlRemappings = []; + protected $children = []; + protected $allowFalse = \false; + protected $allowNewKeys = \true; + protected $addIfNotSet = \false; + protected $performDeepMerging = \true; + protected $ignoreExtraKeys = \false; + protected $removeExtraKeys = \true; + protected $normalizeKeys = \true; + public function setNormalizeKeys($normalizeKeys) + { + $this->normalizeKeys = (bool) $normalizeKeys; + } + /** + * {@inheritdoc} + * + * Namely, you mostly have foo_bar in YAML while you have foo-bar in XML. + * After running this method, all keys are normalized to foo_bar. + * + * If you have a mixed key like foo-bar_moo, it will not be altered. + * The key will also not be altered if the target key already exists. + */ + protected function preNormalize($value) + { + if (!$this->normalizeKeys || !\is_array($value)) { + return $value; + } + $normalized = []; + foreach ($value as $k => $v) { + if (\false !== \strpos($k, '-') && \false === \strpos($k, '_') && !\array_key_exists($normalizedKey = \str_replace('-', '_', $k), $value)) { + $normalized[$normalizedKey] = $v; + } else { + $normalized[$k] = $v; + } + } + return $normalized; + } + /** + * Retrieves the children of this node. + * + * @return array The children + */ + public function getChildren() + { + return $this->children; + } + /** + * Sets the xml remappings that should be performed. + * + * @param array $remappings An array of the form [[string, string]] + */ + public function setXmlRemappings(array $remappings) + { + $this->xmlRemappings = $remappings; + } + /** + * Gets the xml remappings that should be performed. + * + * @return array an array of the form [[string, string]] + */ + public function getXmlRemappings() + { + return $this->xmlRemappings; + } + /** + * Sets whether to add default values for this array if it has not been + * defined in any of the configuration files. + * + * @param bool $boolean + */ + public function setAddIfNotSet($boolean) + { + $this->addIfNotSet = (bool) $boolean; + } + /** + * Sets whether false is allowed as value indicating that the array should be unset. + * + * @param bool $allow + */ + public function setAllowFalse($allow) + { + $this->allowFalse = (bool) $allow; + } + /** + * Sets whether new keys can be defined in subsequent configurations. + * + * @param bool $allow + */ + public function setAllowNewKeys($allow) + { + $this->allowNewKeys = (bool) $allow; + } + /** + * Sets if deep merging should occur. + * + * @param bool $boolean + */ + public function setPerformDeepMerging($boolean) + { + $this->performDeepMerging = (bool) $boolean; + } + /** + * Whether extra keys should just be ignored without an exception. + * + * @param bool $boolean To allow extra keys + * @param bool $remove To remove extra keys + */ + public function setIgnoreExtraKeys($boolean, $remove = \true) + { + $this->ignoreExtraKeys = (bool) $boolean; + $this->removeExtraKeys = $this->ignoreExtraKeys && $remove; + } + /** + * {@inheritdoc} + */ + public function setName($name) + { + $this->name = $name; + } + /** + * {@inheritdoc} + */ + public function hasDefaultValue() + { + return $this->addIfNotSet; + } + /** + * {@inheritdoc} + */ + public function getDefaultValue() + { + if (!$this->hasDefaultValue()) { + throw new \RuntimeException(\sprintf('The node at path "%s" has no default value.', $this->getPath())); + } + $defaults = []; + foreach ($this->children as $name => $child) { + if ($child->hasDefaultValue()) { + $defaults[$name] = $child->getDefaultValue(); + } + } + return $defaults; + } + /** + * Adds a child node. + * + * @throws \InvalidArgumentException when the child node has no name + * @throws \InvalidArgumentException when the child node's name is not unique + */ + public function addChild(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\NodeInterface $node) + { + $name = $node->getName(); + if (!\strlen($name)) { + throw new \InvalidArgumentException('Child nodes must be named.'); + } + if (isset($this->children[$name])) { + throw new \InvalidArgumentException(\sprintf('A child node named "%s" already exists.', $name)); + } + $this->children[$name] = $node; + } + /** + * Finalizes the value of this node. + * + * @param mixed $value + * + * @return mixed The finalised value + * + * @throws UnsetKeyException + * @throws InvalidConfigurationException if the node doesn't have enough children + */ + protected function finalizeValue($value) + { + if (\false === $value) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\UnsetKeyException(\sprintf('Unsetting key for path "%s", value: %s', $this->getPath(), \json_encode($value))); + } + foreach ($this->children as $name => $child) { + if (!\array_key_exists($name, $value)) { + if ($child->isRequired()) { + $ex = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException(\sprintf('The child node "%s" at path "%s" must be configured.', $name, $this->getPath())); + $ex->setPath($this->getPath()); + throw $ex; + } + if ($child->hasDefaultValue()) { + $value[$name] = $child->getDefaultValue(); + } + continue; + } + if ($child->isDeprecated()) { + @\trigger_error($child->getDeprecationMessage($name, $this->getPath()), \E_USER_DEPRECATED); + } + try { + $value[$name] = $child->finalize($value[$name]); + } catch (\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\UnsetKeyException $e) { + unset($value[$name]); + } + } + return $value; + } + /** + * Validates the type of the value. + * + * @param mixed $value + * + * @throws InvalidTypeException + */ + protected function validateType($value) + { + if (!\is_array($value) && (!$this->allowFalse || \false !== $value)) { + $ex = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidTypeException(\sprintf('Invalid type for path "%s". Expected array, but got %s', $this->getPath(), \gettype($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + throw $ex; + } + } + /** + * Normalizes the value. + * + * @param mixed $value The value to normalize + * + * @return mixed The normalized value + * + * @throws InvalidConfigurationException + */ + protected function normalizeValue($value) + { + if (\false === $value) { + return $value; + } + $value = $this->remapXml($value); + $normalized = []; + foreach ($value as $name => $val) { + if (isset($this->children[$name])) { + try { + $normalized[$name] = $this->children[$name]->normalize($val); + } catch (\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\UnsetKeyException $e) { + } + unset($value[$name]); + } elseif (!$this->removeExtraKeys) { + $normalized[$name] = $val; + } + } + // if extra fields are present, throw exception + if (\count($value) && !$this->ignoreExtraKeys) { + $ex = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException(\sprintf('Unrecognized option%s "%s" under "%s"', 1 === \count($value) ? '' : 's', \implode(', ', \array_keys($value)), $this->getPath())); + $ex->setPath($this->getPath()); + throw $ex; + } + return $normalized; + } + /** + * Remaps multiple singular values to a single plural value. + * + * @param array $value The source values + * + * @return array The remapped values + */ + protected function remapXml($value) + { + foreach ($this->xmlRemappings as list($singular, $plural)) { + if (!isset($value[$singular])) { + continue; + } + $value[$plural] = \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Processor::normalizeConfig($value, $singular, $plural); + unset($value[$singular]); + } + return $value; + } + /** + * Merges values together. + * + * @param mixed $leftSide The left side to merge + * @param mixed $rightSide The right side to merge + * + * @return mixed The merged values + * + * @throws InvalidConfigurationException + * @throws \RuntimeException + */ + protected function mergeValues($leftSide, $rightSide) + { + if (\false === $rightSide) { + // if this is still false after the last config has been merged the + // finalization pass will take care of removing this key entirely + return \false; + } + if (\false === $leftSide || !$this->performDeepMerging) { + return $rightSide; + } + foreach ($rightSide as $k => $v) { + // no conflict + if (!\array_key_exists($k, $leftSide)) { + if (!$this->allowNewKeys) { + $ex = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException(\sprintf('You are not allowed to define new elements for path "%s". Please define all elements for this path in one config file. If you are trying to overwrite an element, make sure you redefine it with the same name.', $this->getPath())); + $ex->setPath($this->getPath()); + throw $ex; + } + $leftSide[$k] = $v; + continue; + } + if (!isset($this->children[$k])) { + throw new \RuntimeException('merge() expects a normalized config array.'); + } + $leftSide[$k] = $this->children[$k]->merge($leftSide[$k], $v); + } + return $leftSide; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/BaseNode.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/BaseNode.php new file mode 100755 index 00000000..6734eccf --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/BaseNode.php @@ -0,0 +1,336 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\Exception; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidTypeException; +/** + * The base node class. + * + * @author Johannes M. Schmitt + */ +abstract class BaseNode implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\NodeInterface +{ + protected $name; + protected $parent; + protected $normalizationClosures = []; + protected $finalValidationClosures = []; + protected $allowOverwrite = \true; + protected $required = \false; + protected $deprecationMessage = null; + protected $equivalentValues = []; + protected $attributes = []; + /** + * @param string|null $name The name of the node + * @param NodeInterface|null $parent The parent of this node + * + * @throws \InvalidArgumentException if the name contains a period + */ + public function __construct($name, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\NodeInterface $parent = null) + { + if (\false !== \strpos($name = (string) $name, '.')) { + throw new \InvalidArgumentException('The name must not contain ".".'); + } + $this->name = $name; + $this->parent = $parent; + } + /** + * @param string $key + */ + public function setAttribute($key, $value) + { + $this->attributes[$key] = $value; + } + /** + * @param string $key + * + * @return mixed + */ + public function getAttribute($key, $default = null) + { + return isset($this->attributes[$key]) ? $this->attributes[$key] : $default; + } + /** + * @param string $key + * + * @return bool + */ + public function hasAttribute($key) + { + return isset($this->attributes[$key]); + } + /** + * @return array + */ + public function getAttributes() + { + return $this->attributes; + } + public function setAttributes(array $attributes) + { + $this->attributes = $attributes; + } + /** + * @param string $key + */ + public function removeAttribute($key) + { + unset($this->attributes[$key]); + } + /** + * Sets an info message. + * + * @param string $info + */ + public function setInfo($info) + { + $this->setAttribute('info', $info); + } + /** + * Returns info message. + * + * @return string|null The info text + */ + public function getInfo() + { + return $this->getAttribute('info'); + } + /** + * Sets the example configuration for this node. + * + * @param string|array $example + */ + public function setExample($example) + { + $this->setAttribute('example', $example); + } + /** + * Retrieves the example configuration for this node. + * + * @return string|array|null The example + */ + public function getExample() + { + return $this->getAttribute('example'); + } + /** + * Adds an equivalent value. + * + * @param mixed $originalValue + * @param mixed $equivalentValue + */ + public function addEquivalentValue($originalValue, $equivalentValue) + { + $this->equivalentValues[] = [$originalValue, $equivalentValue]; + } + /** + * Set this node as required. + * + * @param bool $boolean Required node + */ + public function setRequired($boolean) + { + $this->required = (bool) $boolean; + } + /** + * Sets this node as deprecated. + * + * You can use %node% and %path% placeholders in your message to display, + * respectively, the node name and its complete path. + * + * @param string|null $message Deprecated message + */ + public function setDeprecated($message) + { + $this->deprecationMessage = $message; + } + /** + * Sets if this node can be overridden. + * + * @param bool $allow + */ + public function setAllowOverwrite($allow) + { + $this->allowOverwrite = (bool) $allow; + } + /** + * Sets the closures used for normalization. + * + * @param \Closure[] $closures An array of Closures used for normalization + */ + public function setNormalizationClosures(array $closures) + { + $this->normalizationClosures = $closures; + } + /** + * Sets the closures used for final validation. + * + * @param \Closure[] $closures An array of Closures used for final validation + */ + public function setFinalValidationClosures(array $closures) + { + $this->finalValidationClosures = $closures; + } + /** + * {@inheritdoc} + */ + public function isRequired() + { + return $this->required; + } + /** + * Checks if this node is deprecated. + * + * @return bool + */ + public function isDeprecated() + { + return null !== $this->deprecationMessage; + } + /** + * Returns the deprecated message. + * + * @param string $node the configuration node name + * @param string $path the path of the node + * + * @return string + */ + public function getDeprecationMessage($node, $path) + { + return \strtr($this->deprecationMessage, ['%node%' => $node, '%path%' => $path]); + } + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + /** + * {@inheritdoc} + */ + public function getPath() + { + $path = $this->name; + if (null !== $this->parent) { + $path = $this->parent->getPath() . '.' . $path; + } + return $path; + } + /** + * {@inheritdoc} + */ + public final function merge($leftSide, $rightSide) + { + if (!$this->allowOverwrite) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException(\sprintf('Configuration path "%s" cannot be overwritten. You have to define all options for this path, and any of its sub-paths in one configuration section.', $this->getPath())); + } + $this->validateType($leftSide); + $this->validateType($rightSide); + return $this->mergeValues($leftSide, $rightSide); + } + /** + * {@inheritdoc} + */ + public final function normalize($value) + { + $value = $this->preNormalize($value); + // run custom normalization closures + foreach ($this->normalizationClosures as $closure) { + $value = $closure($value); + } + // replace value with their equivalent + foreach ($this->equivalentValues as $data) { + if ($data[0] === $value) { + $value = $data[1]; + } + } + // validate type + $this->validateType($value); + // normalize value + return $this->normalizeValue($value); + } + /** + * Normalizes the value before any other normalization is applied. + * + * @param mixed $value + * + * @return mixed The normalized array value + */ + protected function preNormalize($value) + { + return $value; + } + /** + * Returns parent node for this node. + * + * @return NodeInterface|null + */ + public function getParent() + { + return $this->parent; + } + /** + * {@inheritdoc} + */ + public final function finalize($value) + { + $this->validateType($value); + $value = $this->finalizeValue($value); + // Perform validation on the final value if a closure has been set. + // The closure is also allowed to return another value. + foreach ($this->finalValidationClosures as $closure) { + try { + $value = $closure($value); + } catch (\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\Exception $e) { + throw $e; + } catch (\Exception $e) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException(\sprintf('Invalid configuration for path "%s": %s', $this->getPath(), $e->getMessage()), $e->getCode(), $e); + } + } + return $value; + } + /** + * Validates the type of a Node. + * + * @param mixed $value The value to validate + * + * @throws InvalidTypeException when the value is invalid + */ + protected abstract function validateType($value); + /** + * Normalizes the value. + * + * @param mixed $value The value to normalize + * + * @return mixed The normalized value + */ + protected abstract function normalizeValue($value); + /** + * Merges two values together. + * + * @param mixed $leftSide + * @param mixed $rightSide + * + * @return mixed The merged value + */ + protected abstract function mergeValues($leftSide, $rightSide); + /** + * Finalizes a value. + * + * @param mixed $value The value to finalize + * + * @return mixed The finalized value + */ + protected abstract function finalizeValue($value); +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/BooleanNode.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/BooleanNode.php new file mode 100755 index 00000000..bc4a542b --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/BooleanNode.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidTypeException; +/** + * This node represents a Boolean value in the config tree. + * + * @author Johannes M. Schmitt + */ +class BooleanNode extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ScalarNode +{ + /** + * {@inheritdoc} + */ + protected function validateType($value) + { + if (!\is_bool($value)) { + $ex = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidTypeException(\sprintf('Invalid type for path "%s". Expected boolean, but got %s.', $this->getPath(), \gettype($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + throw $ex; + } + } + /** + * {@inheritdoc} + */ + protected function isValueEmpty($value) + { + // a boolean value cannot be empty + return \false; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php new file mode 100755 index 00000000..b271c877 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php @@ -0,0 +1,432 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ArrayNode; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\PrototypedArrayNode; +/** + * This class provides a fluent interface for defining an array node. + * + * @author Johannes M. Schmitt + */ +class ArrayNodeDefinition extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NodeDefinition implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\ParentNodeDefinitionInterface +{ + protected $performDeepMerging = \true; + protected $ignoreExtraKeys = \false; + protected $removeExtraKeys = \true; + protected $children = []; + protected $prototype; + protected $atLeastOne = \false; + protected $allowNewKeys = \true; + protected $key; + protected $removeKeyItem; + protected $addDefaults = \false; + protected $addDefaultChildren = \false; + protected $nodeBuilder; + protected $normalizeKeys = \true; + /** + * {@inheritdoc} + */ + public function __construct($name, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NodeParentInterface $parent = null) + { + parent::__construct($name, $parent); + $this->nullEquivalent = []; + $this->trueEquivalent = []; + } + /** + * {@inheritdoc} + */ + public function setBuilder(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NodeBuilder $builder) + { + $this->nodeBuilder = $builder; + } + /** + * {@inheritdoc} + */ + public function children() + { + return $this->getNodeBuilder(); + } + /** + * Sets a prototype for child nodes. + * + * @param string $type The type of node + * + * @return NodeDefinition + */ + public function prototype($type) + { + return $this->prototype = $this->getNodeBuilder()->node(null, $type)->setParent($this); + } + /** + * @return VariableNodeDefinition + */ + public function variablePrototype() + { + return $this->prototype('variable'); + } + /** + * @return ScalarNodeDefinition + */ + public function scalarPrototype() + { + return $this->prototype('scalar'); + } + /** + * @return BooleanNodeDefinition + */ + public function booleanPrototype() + { + return $this->prototype('boolean'); + } + /** + * @return IntegerNodeDefinition + */ + public function integerPrototype() + { + return $this->prototype('integer'); + } + /** + * @return FloatNodeDefinition + */ + public function floatPrototype() + { + return $this->prototype('float'); + } + /** + * @return ArrayNodeDefinition + */ + public function arrayPrototype() + { + return $this->prototype('array'); + } + /** + * @return EnumNodeDefinition + */ + public function enumPrototype() + { + return $this->prototype('enum'); + } + /** + * Adds the default value if the node is not set in the configuration. + * + * This method is applicable to concrete nodes only (not to prototype nodes). + * If this function has been called and the node is not set during the finalization + * phase, it's default value will be derived from its children default values. + * + * @return $this + */ + public function addDefaultsIfNotSet() + { + $this->addDefaults = \true; + return $this; + } + /** + * Adds children with a default value when none are defined. + * + * This method is applicable to prototype nodes only. + * + * @param int|string|array|null $children The number of children|The child name|The children names to be added + * + * @return $this + */ + public function addDefaultChildrenIfNoneSet($children = null) + { + $this->addDefaultChildren = $children; + return $this; + } + /** + * Requires the node to have at least one element. + * + * This method is applicable to prototype nodes only. + * + * @return $this + */ + public function requiresAtLeastOneElement() + { + $this->atLeastOne = \true; + return $this; + } + /** + * Disallows adding news keys in a subsequent configuration. + * + * If used all keys have to be defined in the same configuration file. + * + * @return $this + */ + public function disallowNewKeysInSubsequentConfigs() + { + $this->allowNewKeys = \false; + return $this; + } + /** + * Sets a normalization rule for XML configurations. + * + * @param string $singular The key to remap + * @param string $plural The plural of the key for irregular plurals + * + * @return $this + */ + public function fixXmlConfig($singular, $plural = null) + { + $this->normalization()->remap($singular, $plural); + return $this; + } + /** + * Sets the attribute which value is to be used as key. + * + * This is useful when you have an indexed array that should be an + * associative array. You can select an item from within the array + * to be the key of the particular item. For example, if "id" is the + * "key", then: + * + * [ + * ['id' => 'my_name', 'foo' => 'bar'], + * ]; + * + * becomes + * + * [ + * 'my_name' => ['foo' => 'bar'], + * ]; + * + * If you'd like "'id' => 'my_name'" to still be present in the resulting + * array, then you can set the second argument of this method to false. + * + * This method is applicable to prototype nodes only. + * + * @param string $name The name of the key + * @param bool $removeKeyItem Whether or not the key item should be removed + * + * @return $this + */ + public function useAttributeAsKey($name, $removeKeyItem = \true) + { + $this->key = $name; + $this->removeKeyItem = $removeKeyItem; + return $this; + } + /** + * Sets whether the node can be unset. + * + * @param bool $allow + * + * @return $this + */ + public function canBeUnset($allow = \true) + { + $this->merge()->allowUnset($allow); + return $this; + } + /** + * Adds an "enabled" boolean to enable the current section. + * + * By default, the section is disabled. If any configuration is specified then + * the node will be automatically enabled: + * + * enableableArrayNode: {enabled: true, ...} # The config is enabled & default values get overridden + * enableableArrayNode: ~ # The config is enabled & use the default values + * enableableArrayNode: true # The config is enabled & use the default values + * enableableArrayNode: {other: value, ...} # The config is enabled & default values get overridden + * enableableArrayNode: {enabled: false, ...} # The config is disabled + * enableableArrayNode: false # The config is disabled + * + * @return $this + */ + public function canBeEnabled() + { + $this->addDefaultsIfNotSet()->treatFalseLike(['enabled' => \false])->treatTrueLike(['enabled' => \true])->treatNullLike(['enabled' => \true])->beforeNormalization()->ifArray()->then(function ($v) { + $v['enabled'] = isset($v['enabled']) ? $v['enabled'] : \true; + return $v; + })->end()->children()->booleanNode('enabled')->defaultFalse(); + return $this; + } + /** + * Adds an "enabled" boolean to enable the current section. + * + * By default, the section is enabled. + * + * @return $this + */ + public function canBeDisabled() + { + $this->addDefaultsIfNotSet()->treatFalseLike(['enabled' => \false])->treatTrueLike(['enabled' => \true])->treatNullLike(['enabled' => \true])->children()->booleanNode('enabled')->defaultTrue(); + return $this; + } + /** + * Disables the deep merging of the node. + * + * @return $this + */ + public function performNoDeepMerging() + { + $this->performDeepMerging = \false; + return $this; + } + /** + * Allows extra config keys to be specified under an array without + * throwing an exception. + * + * Those config values are ignored and removed from the resulting + * array. This should be used only in special cases where you want + * to send an entire configuration array through a special tree that + * processes only part of the array. + * + * @param bool $remove Whether to remove the extra keys + * + * @return $this + */ + public function ignoreExtraKeys($remove = \true) + { + $this->ignoreExtraKeys = \true; + $this->removeExtraKeys = $remove; + return $this; + } + /** + * Sets key normalization. + * + * @param bool $bool Whether to enable key normalization + * + * @return $this + */ + public function normalizeKeys($bool) + { + $this->normalizeKeys = (bool) $bool; + return $this; + } + /** + * {@inheritdoc} + */ + public function append(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NodeDefinition $node) + { + $this->children[$node->name] = $node->setParent($this); + return $this; + } + /** + * Returns a node builder to be used to add children and prototype. + * + * @return NodeBuilder The node builder + */ + protected function getNodeBuilder() + { + if (null === $this->nodeBuilder) { + $this->nodeBuilder = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NodeBuilder(); + } + return $this->nodeBuilder->setParent($this); + } + /** + * {@inheritdoc} + */ + protected function createNode() + { + if (null === $this->prototype) { + $node = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ArrayNode($this->name, $this->parent); + $this->validateConcreteNode($node); + $node->setAddIfNotSet($this->addDefaults); + foreach ($this->children as $child) { + $child->parent = $node; + $node->addChild($child->getNode()); + } + } else { + $node = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\PrototypedArrayNode($this->name, $this->parent); + $this->validatePrototypeNode($node); + if (null !== $this->key) { + $node->setKeyAttribute($this->key, $this->removeKeyItem); + } + if (\false === $this->allowEmptyValue) { + @\trigger_error(\sprintf('Using %s::cannotBeEmpty() at path "%s" has no effect, consider requiresAtLeastOneElement() instead. In 4.0 both methods will behave the same.', __CLASS__, $node->getPath()), \E_USER_DEPRECATED); + } + if (\true === $this->atLeastOne) { + $node->setMinNumberOfElements(1); + } + if ($this->default) { + $node->setDefaultValue($this->defaultValue); + } + if (\false !== $this->addDefaultChildren) { + $node->setAddChildrenIfNoneSet($this->addDefaultChildren); + if ($this->prototype instanceof static && null === $this->prototype->prototype) { + $this->prototype->addDefaultsIfNotSet(); + } + } + $this->prototype->parent = $node; + $node->setPrototype($this->prototype->getNode()); + } + $node->setAllowNewKeys($this->allowNewKeys); + $node->addEquivalentValue(null, $this->nullEquivalent); + $node->addEquivalentValue(\true, $this->trueEquivalent); + $node->addEquivalentValue(\false, $this->falseEquivalent); + $node->setPerformDeepMerging($this->performDeepMerging); + $node->setRequired($this->required); + $node->setDeprecated($this->deprecationMessage); + $node->setIgnoreExtraKeys($this->ignoreExtraKeys, $this->removeExtraKeys); + $node->setNormalizeKeys($this->normalizeKeys); + if (null !== $this->normalization) { + $node->setNormalizationClosures($this->normalization->before); + $node->setXmlRemappings($this->normalization->remappings); + } + if (null !== $this->merge) { + $node->setAllowOverwrite($this->merge->allowOverwrite); + $node->setAllowFalse($this->merge->allowFalse); + } + if (null !== $this->validation) { + $node->setFinalValidationClosures($this->validation->rules); + } + return $node; + } + /** + * Validate the configuration of a concrete node. + * + * @throws InvalidDefinitionException + */ + protected function validateConcreteNode(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ArrayNode $node) + { + $path = $node->getPath(); + if (null !== $this->key) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidDefinitionException(\sprintf('->useAttributeAsKey() is not applicable to concrete nodes at path "%s"', $path)); + } + if (\false === $this->allowEmptyValue) { + @\trigger_error(\sprintf('->cannotBeEmpty() is not applicable to concrete nodes at path "%s". In 4.0 it will throw an exception.', $path), \E_USER_DEPRECATED); + } + if (\true === $this->atLeastOne) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidDefinitionException(\sprintf('->requiresAtLeastOneElement() is not applicable to concrete nodes at path "%s"', $path)); + } + if ($this->default) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidDefinitionException(\sprintf('->defaultValue() is not applicable to concrete nodes at path "%s"', $path)); + } + if (\false !== $this->addDefaultChildren) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidDefinitionException(\sprintf('->addDefaultChildrenIfNoneSet() is not applicable to concrete nodes at path "%s"', $path)); + } + } + /** + * Validate the configuration of a prototype node. + * + * @throws InvalidDefinitionException + */ + protected function validatePrototypeNode(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\PrototypedArrayNode $node) + { + $path = $node->getPath(); + if ($this->addDefaults) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidDefinitionException(\sprintf('->addDefaultsIfNotSet() is not applicable to prototype nodes at path "%s"', $path)); + } + if (\false !== $this->addDefaultChildren) { + if ($this->default) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidDefinitionException(\sprintf('A default value and default children might not be used together at path "%s"', $path)); + } + if (null !== $this->key && (null === $this->addDefaultChildren || \is_int($this->addDefaultChildren) && $this->addDefaultChildren > 0)) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidDefinitionException(\sprintf('->addDefaultChildrenIfNoneSet() should set default children names as ->useAttributeAsKey() is used at path "%s"', $path)); + } + if (null === $this->key && (\is_string($this->addDefaultChildren) || \is_array($this->addDefaultChildren))) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidDefinitionException(\sprintf('->addDefaultChildrenIfNoneSet() might not set default children names as ->useAttributeAsKey() is not used at path "%s"', $path)); + } + } + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/BooleanNodeDefinition.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/BooleanNodeDefinition.php new file mode 100755 index 00000000..e912fc16 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/BooleanNodeDefinition.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\BooleanNode; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; +/** + * This class provides a fluent interface for defining a node. + * + * @author Johannes M. Schmitt + */ +class BooleanNodeDefinition extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition +{ + /** + * {@inheritdoc} + */ + public function __construct($name, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NodeParentInterface $parent = null) + { + parent::__construct($name, $parent); + $this->nullEquivalent = \true; + } + /** + * Instantiate a Node. + * + * @return BooleanNode The node + */ + protected function instantiateNode() + { + return new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\BooleanNode($this->name, $this->parent); + } + /** + * {@inheritdoc} + * + * @throws InvalidDefinitionException + */ + public function cannotBeEmpty() + { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidDefinitionException('->cannotBeEmpty() is not applicable to BooleanNodeDefinition.'); + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/EnumNodeDefinition.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/EnumNodeDefinition.php new file mode 100755 index 00000000..4580c222 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/EnumNodeDefinition.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\EnumNode; +/** + * Enum Node Definition. + * + * @author Johannes M. Schmitt + */ +class EnumNodeDefinition extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition +{ + private $values; + /** + * @return $this + */ + public function values(array $values) + { + $values = \array_unique($values); + if (empty($values)) { + throw new \InvalidArgumentException('->values() must be called with at least one value.'); + } + $this->values = $values; + return $this; + } + /** + * Instantiate a Node. + * + * @return EnumNode The node + * + * @throws \RuntimeException + */ + protected function instantiateNode() + { + if (null === $this->values) { + throw new \RuntimeException('You must call ->values() on enum nodes.'); + } + return new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\EnumNode($this->name, $this->parent, $this->values); + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/ExprBuilder.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/ExprBuilder.php new file mode 100755 index 00000000..a2b561c1 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/ExprBuilder.php @@ -0,0 +1,239 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\UnsetKeyException; +/** + * This class builds an if expression. + * + * @author Johannes M. Schmitt + * @author Christophe Coevoet + */ +class ExprBuilder +{ + protected $node; + public $ifPart; + public $thenPart; + public function __construct(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NodeDefinition $node) + { + $this->node = $node; + } + /** + * Marks the expression as being always used. + * + * @return $this + */ + public function always(\Closure $then = null) + { + $this->ifPart = function ($v) { + return \true; + }; + if (null !== $then) { + $this->thenPart = $then; + } + return $this; + } + /** + * Sets a closure to use as tests. + * + * The default one tests if the value is true. + * + * @return $this + */ + public function ifTrue(\Closure $closure = null) + { + if (null === $closure) { + $closure = function ($v) { + return \true === $v; + }; + } + $this->ifPart = $closure; + return $this; + } + /** + * Tests if the value is a string. + * + * @return $this + */ + public function ifString() + { + $this->ifPart = function ($v) { + return \is_string($v); + }; + return $this; + } + /** + * Tests if the value is null. + * + * @return $this + */ + public function ifNull() + { + $this->ifPart = function ($v) { + return null === $v; + }; + return $this; + } + /** + * Tests if the value is empty. + * + * @return ExprBuilder + */ + public function ifEmpty() + { + $this->ifPart = function ($v) { + return empty($v); + }; + return $this; + } + /** + * Tests if the value is an array. + * + * @return $this + */ + public function ifArray() + { + $this->ifPart = function ($v) { + return \is_array($v); + }; + return $this; + } + /** + * Tests if the value is in an array. + * + * @return $this + */ + public function ifInArray(array $array) + { + $this->ifPart = function ($v) use($array) { + return \in_array($v, $array, \true); + }; + return $this; + } + /** + * Tests if the value is not in an array. + * + * @return $this + */ + public function ifNotInArray(array $array) + { + $this->ifPart = function ($v) use($array) { + return !\in_array($v, $array, \true); + }; + return $this; + } + /** + * Transforms variables of any type into an array. + * + * @return $this + */ + public function castToArray() + { + $this->ifPart = function ($v) { + return !\is_array($v); + }; + $this->thenPart = function ($v) { + return [$v]; + }; + return $this; + } + /** + * Sets the closure to run if the test pass. + * + * @return $this + */ + public function then(\Closure $closure) + { + $this->thenPart = $closure; + return $this; + } + /** + * Sets a closure returning an empty array. + * + * @return $this + */ + public function thenEmptyArray() + { + $this->thenPart = function ($v) { + return []; + }; + return $this; + } + /** + * Sets a closure marking the value as invalid at processing time. + * + * if you want to add the value of the node in your message just use a %s placeholder. + * + * @param string $message + * + * @return $this + * + * @throws \InvalidArgumentException + */ + public function thenInvalid($message) + { + $this->thenPart = function ($v) use($message) { + throw new \InvalidArgumentException(\sprintf($message, \json_encode($v))); + }; + return $this; + } + /** + * Sets a closure unsetting this key of the array at processing time. + * + * @return $this + * + * @throws UnsetKeyException + */ + public function thenUnset() + { + $this->thenPart = function ($v) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\UnsetKeyException('Unsetting key'); + }; + return $this; + } + /** + * Returns the related node. + * + * @return NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition + * + * @throws \RuntimeException + */ + public function end() + { + if (null === $this->ifPart) { + throw new \RuntimeException('You must specify an if part.'); + } + if (null === $this->thenPart) { + throw new \RuntimeException('You must specify a then part.'); + } + return $this->node; + } + /** + * Builds the expressions. + * + * @param ExprBuilder[] $expressions An array of ExprBuilder instances to build + * + * @return array + */ + public static function buildExpressions(array $expressions) + { + foreach ($expressions as $k => $expr) { + if ($expr instanceof self) { + $if = $expr->ifPart; + $then = $expr->thenPart; + $expressions[$k] = function ($v) use($if, $then) { + return $if($v) ? $then($v) : $v; + }; + } + } + return $expressions; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/FloatNodeDefinition.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/FloatNodeDefinition.php new file mode 100755 index 00000000..3a3e7d14 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/FloatNodeDefinition.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\FloatNode; +/** + * This class provides a fluent interface for defining a float node. + * + * @author Jeanmonod David + */ +class FloatNodeDefinition extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NumericNodeDefinition +{ + /** + * Instantiates a Node. + * + * @return FloatNode The node + */ + protected function instantiateNode() + { + return new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\FloatNode($this->name, $this->parent, $this->min, $this->max); + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/IntegerNodeDefinition.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/IntegerNodeDefinition.php new file mode 100755 index 00000000..f29ba047 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/IntegerNodeDefinition.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\IntegerNode; +/** + * This class provides a fluent interface for defining an integer node. + * + * @author Jeanmonod David + */ +class IntegerNodeDefinition extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NumericNodeDefinition +{ + /** + * Instantiates a Node. + * + * @return IntegerNode The node + */ + protected function instantiateNode() + { + return new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\IntegerNode($this->name, $this->parent, $this->min, $this->max); + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/MergeBuilder.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/MergeBuilder.php new file mode 100755 index 00000000..c4fa0da5 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/MergeBuilder.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder; + +/** + * This class builds merge conditions. + * + * @author Johannes M. Schmitt + */ +class MergeBuilder +{ + protected $node; + public $allowFalse = \false; + public $allowOverwrite = \true; + public function __construct(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NodeDefinition $node) + { + $this->node = $node; + } + /** + * Sets whether the node can be unset. + * + * @param bool $allow + * + * @return $this + */ + public function allowUnset($allow = \true) + { + $this->allowFalse = $allow; + return $this; + } + /** + * Sets whether the node can be overwritten. + * + * @param bool $deny Whether the overwriting is forbidden or not + * + * @return $this + */ + public function denyOverwrite($deny = \true) + { + $this->allowOverwrite = !$deny; + return $this; + } + /** + * Returns the related node. + * + * @return NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition + */ + public function end() + { + return $this->node; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/NodeBuilder.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/NodeBuilder.php new file mode 100755 index 00000000..65d67e67 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/NodeBuilder.php @@ -0,0 +1,204 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder; + +/** + * This class provides a fluent interface for building a node. + * + * @author Johannes M. Schmitt + */ +class NodeBuilder implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NodeParentInterface +{ + protected $parent; + protected $nodeMapping; + public function __construct() + { + $this->nodeMapping = ['variable' => __NAMESPACE__ . '\\VariableNodeDefinition', 'scalar' => __NAMESPACE__ . '\\ScalarNodeDefinition', 'boolean' => __NAMESPACE__ . '\\BooleanNodeDefinition', 'integer' => __NAMESPACE__ . '\\IntegerNodeDefinition', 'float' => __NAMESPACE__ . '\\FloatNodeDefinition', 'array' => __NAMESPACE__ . '\\ArrayNodeDefinition', 'enum' => __NAMESPACE__ . '\\EnumNodeDefinition']; + } + /** + * Set the parent node. + * + * @return $this + */ + public function setParent(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\ParentNodeDefinitionInterface $parent = null) + { + $this->parent = $parent; + return $this; + } + /** + * Creates a child array node. + * + * @param string $name The name of the node + * + * @return ArrayNodeDefinition The child node + */ + public function arrayNode($name) + { + return $this->node($name, 'array'); + } + /** + * Creates a child scalar node. + * + * @param string $name The name of the node + * + * @return ScalarNodeDefinition The child node + */ + public function scalarNode($name) + { + return $this->node($name, 'scalar'); + } + /** + * Creates a child Boolean node. + * + * @param string $name The name of the node + * + * @return BooleanNodeDefinition The child node + */ + public function booleanNode($name) + { + return $this->node($name, 'boolean'); + } + /** + * Creates a child integer node. + * + * @param string $name The name of the node + * + * @return IntegerNodeDefinition The child node + */ + public function integerNode($name) + { + return $this->node($name, 'integer'); + } + /** + * Creates a child float node. + * + * @param string $name The name of the node + * + * @return FloatNodeDefinition The child node + */ + public function floatNode($name) + { + return $this->node($name, 'float'); + } + /** + * Creates a child EnumNode. + * + * @param string $name + * + * @return EnumNodeDefinition + */ + public function enumNode($name) + { + return $this->node($name, 'enum'); + } + /** + * Creates a child variable node. + * + * @param string $name The name of the node + * + * @return VariableNodeDefinition The builder of the child node + */ + public function variableNode($name) + { + return $this->node($name, 'variable'); + } + /** + * Returns the parent node. + * + * @return NodeDefinition&ParentNodeDefinitionInterface The parent node + */ + public function end() + { + return $this->parent; + } + /** + * Creates a child node. + * + * @param string|null $name The name of the node + * @param string $type The type of the node + * + * @return NodeDefinition The child node + * + * @throws \RuntimeException When the node type is not registered + * @throws \RuntimeException When the node class is not found + */ + public function node($name, $type) + { + $class = $this->getNodeClass($type); + $node = new $class($name); + $this->append($node); + return $node; + } + /** + * Appends a node definition. + * + * Usage: + * + * $node = new ArrayNodeDefinition('name') + * ->children() + * ->scalarNode('foo')->end() + * ->scalarNode('baz')->end() + * ->append($this->getBarNodeDefinition()) + * ->end() + * ; + * + * @return $this + */ + public function append(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NodeDefinition $node) + { + if ($node instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\ParentNodeDefinitionInterface) { + $builder = clone $this; + $builder->setParent(null); + $node->setBuilder($builder); + } + if (null !== $this->parent) { + $this->parent->append($node); + // Make this builder the node parent to allow for a fluid interface + $node->setParent($this); + } + return $this; + } + /** + * Adds or overrides a node Type. + * + * @param string $type The name of the type + * @param string $class The fully qualified name the node definition class + * + * @return $this + */ + public function setNodeClass($type, $class) + { + $this->nodeMapping[\strtolower($type)] = $class; + return $this; + } + /** + * Returns the class name of the node definition. + * + * @param string $type The node type + * + * @return string The node definition class name + * + * @throws \RuntimeException When the node type is not registered + * @throws \RuntimeException When the node class is not found + */ + protected function getNodeClass($type) + { + $type = \strtolower($type); + if (!isset($this->nodeMapping[$type])) { + throw new \RuntimeException(\sprintf('The node type "%s" is not registered.', $type)); + } + $class = $this->nodeMapping[$type]; + if (!\class_exists($class)) { + throw new \RuntimeException(\sprintf('The node class "%s" does not exist.', $class)); + } + return $class; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/NodeDefinition.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/NodeDefinition.php new file mode 100755 index 00000000..ad9e2ca4 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/NodeDefinition.php @@ -0,0 +1,310 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\NodeInterface; +/** + * This class provides a fluent interface for defining a node. + * + * @author Johannes M. Schmitt + */ +abstract class NodeDefinition implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NodeParentInterface +{ + protected $name; + protected $normalization; + protected $validation; + protected $defaultValue; + protected $default = \false; + protected $required = \false; + protected $deprecationMessage = null; + protected $merge; + protected $allowEmptyValue = \true; + protected $nullEquivalent; + protected $trueEquivalent = \true; + protected $falseEquivalent = \false; + protected $parent; + protected $attributes = []; + /** + * @param string|null $name The name of the node + * @param NodeParentInterface|null $parent The parent + */ + public function __construct($name, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NodeParentInterface $parent = null) + { + $this->parent = $parent; + $this->name = $name; + } + /** + * Sets the parent node. + * + * @return $this + */ + public function setParent(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NodeParentInterface $parent) + { + $this->parent = $parent; + return $this; + } + /** + * Sets info message. + * + * @param string $info The info text + * + * @return $this + */ + public function info($info) + { + return $this->attribute('info', $info); + } + /** + * Sets example configuration. + * + * @param string|array $example + * + * @return $this + */ + public function example($example) + { + return $this->attribute('example', $example); + } + /** + * Sets an attribute on the node. + * + * @param string $key + * @param mixed $value + * + * @return $this + */ + public function attribute($key, $value) + { + $this->attributes[$key] = $value; + return $this; + } + /** + * Returns the parent node. + * + * @return NodeParentInterface|NodeBuilder|NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition|null The builder of the parent node + */ + public function end() + { + return $this->parent; + } + /** + * Creates the node. + * + * @param bool $forceRootNode Whether to force this node as the root node + * + * @return NodeInterface + */ + public function getNode($forceRootNode = \false) + { + if ($forceRootNode) { + $this->parent = null; + } + if (null !== $this->normalization) { + $this->normalization->before = \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\ExprBuilder::buildExpressions($this->normalization->before); + } + if (null !== $this->validation) { + $this->validation->rules = \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\ExprBuilder::buildExpressions($this->validation->rules); + } + $node = $this->createNode(); + $node->setAttributes($this->attributes); + return $node; + } + /** + * Sets the default value. + * + * @param mixed $value The default value + * + * @return $this + */ + public function defaultValue($value) + { + $this->default = \true; + $this->defaultValue = $value; + return $this; + } + /** + * Sets the node as required. + * + * @return $this + */ + public function isRequired() + { + $this->required = \true; + return $this; + } + /** + * Sets the node as deprecated. + * + * You can use %node% and %path% placeholders in your message to display, + * respectively, the node name and its complete path. + * + * @param string $message Deprecation message + * + * @return $this + */ + public function setDeprecated($message = 'The child node "%node%" at path "%path%" is deprecated.') + { + $this->deprecationMessage = $message; + return $this; + } + /** + * Sets the equivalent value used when the node contains null. + * + * @param mixed $value + * + * @return $this + */ + public function treatNullLike($value) + { + $this->nullEquivalent = $value; + return $this; + } + /** + * Sets the equivalent value used when the node contains true. + * + * @param mixed $value + * + * @return $this + */ + public function treatTrueLike($value) + { + $this->trueEquivalent = $value; + return $this; + } + /** + * Sets the equivalent value used when the node contains false. + * + * @param mixed $value + * + * @return $this + */ + public function treatFalseLike($value) + { + $this->falseEquivalent = $value; + return $this; + } + /** + * Sets null as the default value. + * + * @return $this + */ + public function defaultNull() + { + return $this->defaultValue(null); + } + /** + * Sets true as the default value. + * + * @return $this + */ + public function defaultTrue() + { + return $this->defaultValue(\true); + } + /** + * Sets false as the default value. + * + * @return $this + */ + public function defaultFalse() + { + return $this->defaultValue(\false); + } + /** + * Sets an expression to run before the normalization. + * + * @return ExprBuilder + */ + public function beforeNormalization() + { + return $this->normalization()->before(); + } + /** + * Denies the node value being empty. + * + * @return $this + */ + public function cannotBeEmpty() + { + $this->allowEmptyValue = \false; + return $this; + } + /** + * Sets an expression to run for the validation. + * + * The expression receives the value of the node and must return it. It can + * modify it. + * An exception should be thrown when the node is not valid. + * + * @return ExprBuilder + */ + public function validate() + { + return $this->validation()->rule(); + } + /** + * Sets whether the node can be overwritten. + * + * @param bool $deny Whether the overwriting is forbidden or not + * + * @return $this + */ + public function cannotBeOverwritten($deny = \true) + { + $this->merge()->denyOverwrite($deny); + return $this; + } + /** + * Gets the builder for validation rules. + * + * @return ValidationBuilder + */ + protected function validation() + { + if (null === $this->validation) { + $this->validation = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\ValidationBuilder($this); + } + return $this->validation; + } + /** + * Gets the builder for merging rules. + * + * @return MergeBuilder + */ + protected function merge() + { + if (null === $this->merge) { + $this->merge = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\MergeBuilder($this); + } + return $this->merge; + } + /** + * Gets the builder for normalization rules. + * + * @return NormalizationBuilder + */ + protected function normalization() + { + if (null === $this->normalization) { + $this->normalization = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NormalizationBuilder($this); + } + return $this->normalization; + } + /** + * Instantiate and configure the node according to this definition. + * + * @return NodeInterface The node instance + * + * @throws InvalidDefinitionException When the definition is invalid + */ + protected abstract function createNode(); +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/NodeParentInterface.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/NodeParentInterface.php new file mode 100755 index 00000000..9e6ebd54 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/NodeParentInterface.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder; + +/** + * An interface that must be implemented by all node parents. + * + * @author Victor Berchet + */ +interface NodeParentInterface +{ +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/NormalizationBuilder.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/NormalizationBuilder.php new file mode 100755 index 00000000..97b2568c --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/NormalizationBuilder.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder; + +/** + * This class builds normalization conditions. + * + * @author Johannes M. Schmitt + */ +class NormalizationBuilder +{ + protected $node; + public $before = []; + public $remappings = []; + public function __construct(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NodeDefinition $node) + { + $this->node = $node; + } + /** + * Registers a key to remap to its plural form. + * + * @param string $key The key to remap + * @param string $plural The plural of the key in case of irregular plural + * + * @return $this + */ + public function remap($key, $plural = null) + { + $this->remappings[] = [$key, null === $plural ? $key . 's' : $plural]; + return $this; + } + /** + * Registers a closure to run before the normalization or an expression builder to build it if null is provided. + * + * @return ExprBuilder|$this + */ + public function before(\Closure $closure = null) + { + if (null !== $closure) { + $this->before[] = $closure; + return $this; + } + return $this->before[] = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\ExprBuilder($this->node); + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/NumericNodeDefinition.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/NumericNodeDefinition.php new file mode 100755 index 00000000..f16b29fa --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/NumericNodeDefinition.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; +/** + * Abstract class that contains common code of integer and float node definitions. + * + * @author David Jeanmonod + */ +abstract class NumericNodeDefinition extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition +{ + protected $min; + protected $max; + /** + * Ensures that the value is smaller than the given reference. + * + * @param mixed $max + * + * @return $this + * + * @throws \InvalidArgumentException when the constraint is inconsistent + */ + public function max($max) + { + if (isset($this->min) && $this->min > $max) { + throw new \InvalidArgumentException(\sprintf('You cannot define a max(%s) as you already have a min(%s)', $max, $this->min)); + } + $this->max = $max; + return $this; + } + /** + * Ensures that the value is bigger than the given reference. + * + * @param mixed $min + * + * @return $this + * + * @throws \InvalidArgumentException when the constraint is inconsistent + */ + public function min($min) + { + if (isset($this->max) && $this->max < $min) { + throw new \InvalidArgumentException(\sprintf('You cannot define a min(%s) as you already have a max(%s)', $min, $this->max)); + } + $this->min = $min; + return $this; + } + /** + * {@inheritdoc} + * + * @throws InvalidDefinitionException + */ + public function cannotBeEmpty() + { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidDefinitionException('->cannotBeEmpty() is not applicable to NumericNodeDefinition.'); + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/ParentNodeDefinitionInterface.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/ParentNodeDefinitionInterface.php new file mode 100755 index 00000000..92b84f3d --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/ParentNodeDefinitionInterface.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder; + +/** + * An interface that must be implemented by nodes which can have children. + * + * @author Victor Berchet + */ +interface ParentNodeDefinitionInterface +{ + /** + * Returns a builder to add children nodes. + * + * @return NodeBuilder + */ + public function children(); + /** + * Appends a node definition. + * + * Usage: + * + * $node = $parentNode + * ->children() + * ->scalarNode('foo')->end() + * ->scalarNode('baz')->end() + * ->append($this->getBarNodeDefinition()) + * ->end() + * ; + * + * @return $this + */ + public function append(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NodeDefinition $node); + /** + * Sets a custom children builder. + */ + public function setBuilder(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NodeBuilder $builder); +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/ScalarNodeDefinition.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/ScalarNodeDefinition.php new file mode 100755 index 00000000..2fa1a366 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/ScalarNodeDefinition.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ScalarNode; +/** + * This class provides a fluent interface for defining a node. + * + * @author Johannes M. Schmitt + */ +class ScalarNodeDefinition extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\VariableNodeDefinition +{ + /** + * Instantiate a Node. + * + * @return ScalarNode The node + */ + protected function instantiateNode() + { + return new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ScalarNode($this->name, $this->parent); + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/TreeBuilder.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/TreeBuilder.php new file mode 100755 index 00000000..e6d97d16 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/TreeBuilder.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\NodeInterface; +/** + * This is the entry class for building a config tree. + * + * @author Johannes M. Schmitt + */ +class TreeBuilder implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NodeParentInterface +{ + protected $tree; + protected $root; + /** + * @deprecated since 3.4. To be removed in 4.0 + */ + protected $builder; + /** + * Creates the root node. + * + * @param string $name The name of the root node + * @param string $type The type of the root node + * @param NodeBuilder $builder A custom node builder instance + * + * @return ArrayNodeDefinition|NodeDefinition The root node (as an ArrayNodeDefinition when the type is 'array') + * + * @throws \RuntimeException When the node type is not supported + */ + public function root($name, $type = 'array', \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NodeBuilder $builder = null) + { + $builder = $builder ?: new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NodeBuilder(); + return $this->root = $builder->node($name, $type)->setParent($this); + } + /** + * Builds the tree. + * + * @return NodeInterface + * + * @throws \RuntimeException + */ + public function buildTree() + { + if (null === $this->root) { + throw new \RuntimeException('The configuration tree has no root node.'); + } + if (null !== $this->tree) { + return $this->tree; + } + return $this->tree = $this->root->getNode(\true); + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/ValidationBuilder.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/ValidationBuilder.php new file mode 100755 index 00000000..3ce723a0 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/ValidationBuilder.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder; + +/** + * This class builds validation conditions. + * + * @author Christophe Coevoet + */ +class ValidationBuilder +{ + protected $node; + public $rules = []; + public function __construct(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NodeDefinition $node) + { + $this->node = $node; + } + /** + * Registers a closure to run as normalization or an expression builder to build it if null is provided. + * + * @return ExprBuilder|$this + */ + public function rule(\Closure $closure = null) + { + if (null !== $closure) { + $this->rules[] = $closure; + return $this; + } + return $this->rules[] = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\ExprBuilder($this->node); + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/VariableNodeDefinition.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/VariableNodeDefinition.php new file mode 100755 index 00000000..8ac2b231 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Builder/VariableNodeDefinition.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\VariableNode; +/** + * This class provides a fluent interface for defining a node. + * + * @author Johannes M. Schmitt + */ +class VariableNodeDefinition extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Builder\NodeDefinition +{ + /** + * Instantiate a Node. + * + * @return VariableNode The node + */ + protected function instantiateNode() + { + return new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\VariableNode($this->name, $this->parent); + } + /** + * {@inheritdoc} + */ + protected function createNode() + { + $node = $this->instantiateNode(); + if (null !== $this->normalization) { + $node->setNormalizationClosures($this->normalization->before); + } + if (null !== $this->merge) { + $node->setAllowOverwrite($this->merge->allowOverwrite); + } + if (\true === $this->default) { + $node->setDefaultValue($this->defaultValue); + } + $node->setAllowEmptyValue($this->allowEmptyValue); + $node->addEquivalentValue(null, $this->nullEquivalent); + $node->addEquivalentValue(\true, $this->trueEquivalent); + $node->addEquivalentValue(\false, $this->falseEquivalent); + $node->setRequired($this->required); + $node->setDeprecated($this->deprecationMessage); + if (null !== $this->validation) { + $node->setFinalValidationClosures($this->validation->rules); + } + return $node; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/ConfigurationInterface.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/ConfigurationInterface.php new file mode 100755 index 00000000..9a367786 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/ConfigurationInterface.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition; + +/** + * Configuration interface. + * + * @author Victor Berchet + */ +interface ConfigurationInterface +{ + /** + * Generates the configuration tree builder. + * + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder + */ + public function getConfigTreeBuilder(); +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php new file mode 100755 index 00000000..4c9d434b --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php @@ -0,0 +1,257 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Dumper; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ArrayNode; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ConfigurationInterface; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\EnumNode; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\NodeInterface; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\PrototypedArrayNode; +/** + * Dumps a XML reference configuration for the given configuration/node instance. + * + * @author Wouter J + */ +class XmlReferenceDumper +{ + private $reference; + public function dump(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ConfigurationInterface $configuration, $namespace = null) + { + return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree(), $namespace); + } + public function dumpNode(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\NodeInterface $node, $namespace = null) + { + $this->reference = ''; + $this->writeNode($node, 0, \true, $namespace); + $ref = $this->reference; + $this->reference = null; + return $ref; + } + /** + * @param int $depth + * @param bool $root If the node is the root node + * @param string $namespace The namespace of the node + */ + private function writeNode(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\NodeInterface $node, $depth = 0, $root = \false, $namespace = null) + { + $rootName = $root ? 'config' : $node->getName(); + $rootNamespace = $namespace ?: ($root ? 'http://example.org/schema/dic/' . $node->getName() : null); + // xml remapping + if ($node->getParent()) { + $remapping = \array_filter($node->getParent()->getXmlRemappings(), function ($mapping) use($rootName) { + return $rootName === $mapping[1]; + }); + if (\count($remapping)) { + list($singular) = \current($remapping); + $rootName = $singular; + } + } + $rootName = \str_replace('_', '-', $rootName); + $rootAttributes = []; + $rootAttributeComments = []; + $rootChildren = []; + $rootComments = []; + if ($node instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ArrayNode) { + $children = $node->getChildren(); + // comments about the root node + if ($rootInfo = $node->getInfo()) { + $rootComments[] = $rootInfo; + } + if ($rootNamespace) { + $rootComments[] = 'Namespace: ' . $rootNamespace; + } + // render prototyped nodes + if ($node instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\PrototypedArrayNode) { + $prototype = $node->getPrototype(); + $info = 'prototype'; + if (null !== $prototype->getInfo()) { + $info .= ': ' . $prototype->getInfo(); + } + \array_unshift($rootComments, $info); + if ($key = $node->getKeyAttribute()) { + $rootAttributes[$key] = \str_replace('-', ' ', $rootName) . ' ' . $key; + } + if ($prototype instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\PrototypedArrayNode) { + $prototype->setName($key); + $children = [$key => $prototype]; + } elseif ($prototype instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ArrayNode) { + $children = $prototype->getChildren(); + } else { + if ($prototype->hasDefaultValue()) { + $prototypeValue = $prototype->getDefaultValue(); + } else { + switch (\get_class($prototype)) { + case 'Symfony\\Component\\Config\\Definition\\ScalarNode': + $prototypeValue = 'scalar value'; + break; + case 'Symfony\\Component\\Config\\Definition\\FloatNode': + case 'Symfony\\Component\\Config\\Definition\\IntegerNode': + $prototypeValue = 'numeric value'; + break; + case 'Symfony\\Component\\Config\\Definition\\BooleanNode': + $prototypeValue = 'true|false'; + break; + case 'Symfony\\Component\\Config\\Definition\\EnumNode': + $prototypeValue = \implode('|', \array_map('json_encode', $prototype->getValues())); + break; + default: + $prototypeValue = 'value'; + } + } + } + } + // get attributes and elements + foreach ($children as $child) { + if (!$child instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ArrayNode) { + // get attributes + // metadata + $name = \str_replace('_', '-', $child->getName()); + $value = '%%%%not_defined%%%%'; + // use a string which isn't used in the normal world + // comments + $comments = []; + if ($info = $child->getInfo()) { + $comments[] = $info; + } + if ($example = $child->getExample()) { + $comments[] = 'Example: ' . $example; + } + if ($child->isRequired()) { + $comments[] = 'Required'; + } + if ($child->isDeprecated()) { + $comments[] = \sprintf('Deprecated (%s)', $child->getDeprecationMessage($child->getName(), $node->getPath())); + } + if ($child instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\EnumNode) { + $comments[] = 'One of ' . \implode('; ', \array_map('json_encode', $child->getValues())); + } + if (\count($comments)) { + $rootAttributeComments[$name] = \implode(";\n", $comments); + } + // default values + if ($child->hasDefaultValue()) { + $value = $child->getDefaultValue(); + } + // append attribute + $rootAttributes[$name] = $value; + } else { + // get elements + $rootChildren[] = $child; + } + } + } + // render comments + // root node comment + if (\count($rootComments)) { + foreach ($rootComments as $comment) { + $this->writeLine('', $depth); + } + } + // attribute comments + if (\count($rootAttributeComments)) { + foreach ($rootAttributeComments as $attrName => $comment) { + $commentDepth = $depth + 4 + \strlen($attrName) + 2; + $commentLines = \explode("\n", $comment); + $multiline = \count($commentLines) > 1; + $comment = \implode(\PHP_EOL . \str_repeat(' ', $commentDepth), $commentLines); + if ($multiline) { + $this->writeLine('', $depth); + } else { + $this->writeLine('', $depth); + } + } + } + // render start tag + attributes + $rootIsVariablePrototype = isset($prototypeValue); + $rootIsEmptyTag = 0 === \count($rootChildren) && !$rootIsVariablePrototype; + $rootOpenTag = '<' . $rootName; + if (1 >= ($attributesCount = \count($rootAttributes))) { + if (1 === $attributesCount) { + $rootOpenTag .= \sprintf(' %s="%s"', \current(\array_keys($rootAttributes)), $this->writeValue(\current($rootAttributes))); + } + $rootOpenTag .= $rootIsEmptyTag ? ' />' : '>'; + if ($rootIsVariablePrototype) { + $rootOpenTag .= $prototypeValue . ''; + } + $this->writeLine($rootOpenTag, $depth); + } else { + $this->writeLine($rootOpenTag, $depth); + $i = 1; + foreach ($rootAttributes as $attrName => $attrValue) { + $attr = \sprintf('%s="%s"', $attrName, $this->writeValue($attrValue)); + $this->writeLine($attr, $depth + 4); + if ($attributesCount === $i++) { + $this->writeLine($rootIsEmptyTag ? '/>' : '>', $depth); + if ($rootIsVariablePrototype) { + $rootOpenTag .= $prototypeValue . ''; + } + } + } + } + // render children tags + foreach ($rootChildren as $child) { + $this->writeLine(''); + $this->writeNode($child, $depth + 4); + } + // render end tag + if (!$rootIsEmptyTag && !$rootIsVariablePrototype) { + $this->writeLine(''); + $rootEndTag = ''; + $this->writeLine($rootEndTag, $depth); + } + } + /** + * Outputs a single config reference line. + * + * @param string $text + * @param int $indent + */ + private function writeLine($text, $indent = 0) + { + $indent = \strlen($text) + $indent; + $format = '%' . $indent . 's'; + $this->reference .= \sprintf($format, $text) . \PHP_EOL; + } + /** + * Renders the string conversion of the value. + * + * @param mixed $value + * + * @return string + */ + private function writeValue($value) + { + if ('%%%%not_defined%%%%' === $value) { + return ''; + } + if (\is_string($value) || \is_numeric($value)) { + return $value; + } + if (\false === $value) { + return 'false'; + } + if (\true === $value) { + return 'true'; + } + if (null === $value) { + return 'null'; + } + if (empty($value)) { + return ''; + } + if (\is_array($value)) { + return \implode(',', $value); + } + return ''; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Dumper/YamlReferenceDumper.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Dumper/YamlReferenceDumper.php new file mode 100755 index 00000000..6ca12d96 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Dumper/YamlReferenceDumper.php @@ -0,0 +1,205 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Dumper; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ArrayNode; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ConfigurationInterface; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\EnumNode; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\NodeInterface; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\PrototypedArrayNode; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ScalarNode; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Yaml\Inline; +/** + * Dumps a Yaml reference configuration for the given configuration/node instance. + * + * @author Kevin Bond + */ +class YamlReferenceDumper +{ + private $reference; + public function dump(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ConfigurationInterface $configuration) + { + return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree()); + } + public function dumpAtPath(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ConfigurationInterface $configuration, $path) + { + $rootNode = $node = $configuration->getConfigTreeBuilder()->buildTree(); + foreach (\explode('.', $path) as $step) { + if (!$node instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ArrayNode) { + throw new \UnexpectedValueException(\sprintf('Unable to find node at path "%s.%s"', $rootNode->getName(), $path)); + } + /** @var NodeInterface[] $children */ + $children = $node instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\PrototypedArrayNode ? $this->getPrototypeChildren($node) : $node->getChildren(); + foreach ($children as $child) { + if ($child->getName() === $step) { + $node = $child; + continue 2; + } + } + throw new \UnexpectedValueException(\sprintf('Unable to find node at path "%s.%s"', $rootNode->getName(), $path)); + } + return $this->dumpNode($node); + } + public function dumpNode(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\NodeInterface $node) + { + $this->reference = ''; + $this->writeNode($node); + $ref = $this->reference; + $this->reference = null; + return $ref; + } + /** + * @param int $depth + * @param bool $prototypedArray + */ + private function writeNode(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\NodeInterface $node, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\NodeInterface $parentNode = null, $depth = 0, $prototypedArray = \false) + { + $comments = []; + $default = ''; + $defaultArray = null; + $children = null; + $example = $node->getExample(); + // defaults + if ($node instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ArrayNode) { + $children = $node->getChildren(); + if ($node instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\PrototypedArrayNode) { + $children = $this->getPrototypeChildren($node); + } + if (!$children) { + if ($node->hasDefaultValue() && \count($defaultArray = $node->getDefaultValue())) { + $default = ''; + } elseif (!\is_array($example)) { + $default = '[]'; + } + } + } elseif ($node instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\EnumNode) { + $comments[] = 'One of ' . \implode('; ', \array_map('json_encode', $node->getValues())); + $default = $node->hasDefaultValue() ? \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Yaml\Inline::dump($node->getDefaultValue()) : '~'; + } else { + $default = '~'; + if ($node->hasDefaultValue()) { + $default = $node->getDefaultValue(); + if (\is_array($default)) { + if (\count($defaultArray = $node->getDefaultValue())) { + $default = ''; + } elseif (!\is_array($example)) { + $default = '[]'; + } + } else { + $default = \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Yaml\Inline::dump($default); + } + } + } + // required? + if ($node->isRequired()) { + $comments[] = 'Required'; + } + // deprecated? + if ($node->isDeprecated()) { + $comments[] = \sprintf('Deprecated (%s)', $node->getDeprecationMessage($node->getName(), $parentNode ? $parentNode->getPath() : $node->getPath())); + } + // example + if ($example && !\is_array($example)) { + $comments[] = 'Example: ' . $example; + } + $default = '' != (string) $default ? ' ' . $default : ''; + $comments = \count($comments) ? '# ' . \implode(', ', $comments) : ''; + $key = $prototypedArray ? '-' : $node->getName() . ':'; + $text = \rtrim(\sprintf('%-21s%s %s', $key, $default, $comments), ' '); + if ($info = $node->getInfo()) { + $this->writeLine(''); + // indenting multi-line info + $info = \str_replace("\n", \sprintf("\n%" . $depth * 4 . 's# ', ' '), $info); + $this->writeLine('# ' . $info, $depth * 4); + } + $this->writeLine($text, $depth * 4); + // output defaults + if ($defaultArray) { + $this->writeLine(''); + $message = \count($defaultArray) > 1 ? 'Defaults' : 'Default'; + $this->writeLine('# ' . $message . ':', $depth * 4 + 4); + $this->writeArray($defaultArray, $depth + 1); + } + if (\is_array($example)) { + $this->writeLine(''); + $message = \count($example) > 1 ? 'Examples' : 'Example'; + $this->writeLine('# ' . $message . ':', $depth * 4 + 4); + $this->writeArray($example, $depth + 1); + } + if ($children) { + foreach ($children as $childNode) { + $this->writeNode($childNode, $node, $depth + 1, $node instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\PrototypedArrayNode && !$node->getKeyAttribute()); + } + } + } + /** + * Outputs a single config reference line. + * + * @param string $text + * @param int $indent + */ + private function writeLine($text, $indent = 0) + { + $indent = \strlen($text) + $indent; + $format = '%' . $indent . 's'; + $this->reference .= \sprintf($format, $text) . "\n"; + } + private function writeArray(array $array, $depth) + { + $isIndexed = \array_values($array) === $array; + foreach ($array as $key => $value) { + if (\is_array($value)) { + $val = ''; + } else { + $val = $value; + } + if ($isIndexed) { + $this->writeLine('- ' . $val, $depth * 4); + } else { + $this->writeLine(\sprintf('%-20s %s', $key . ':', $val), $depth * 4); + } + if (\is_array($value)) { + $this->writeArray($value, $depth + 1); + } + } + } + /** + * @return array + */ + private function getPrototypeChildren(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\PrototypedArrayNode $node) + { + $prototype = $node->getPrototype(); + $key = $node->getKeyAttribute(); + // Do not expand prototype if it isn't an array node nor uses attribute as key + if (!$key && !$prototype instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ArrayNode) { + return $node->getChildren(); + } + if ($prototype instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ArrayNode) { + $keyNode = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ArrayNode($key, $node); + $children = $prototype->getChildren(); + if ($prototype instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\PrototypedArrayNode && $prototype->getKeyAttribute()) { + $children = $this->getPrototypeChildren($prototype); + } + // add children + foreach ($children as $childNode) { + $keyNode->addChild($childNode); + } + } else { + $keyNode = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ScalarNode($key, $node); + } + $info = 'Prototype'; + if (null !== $prototype->getInfo()) { + $info .= ': ' . $prototype->getInfo(); + } + $keyNode->setInfo($info); + return [$key => $keyNode]; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/EnumNode.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/EnumNode.php new file mode 100755 index 00000000..0f87659a --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/EnumNode.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +/** + * Node which only allows a finite set of values. + * + * @author Johannes M. Schmitt + */ +class EnumNode extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ScalarNode +{ + private $values; + public function __construct($name, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\NodeInterface $parent = null, array $values = []) + { + $values = \array_unique($values); + if (empty($values)) { + throw new \InvalidArgumentException('$values must contain at least one element.'); + } + parent::__construct($name, $parent); + $this->values = $values; + } + public function getValues() + { + return $this->values; + } + protected function finalizeValue($value) + { + $value = parent::finalizeValue($value); + if (!\in_array($value, $this->values, \true)) { + $ex = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException(\sprintf('The value %s is not allowed for path "%s". Permissible values: %s', \json_encode($value), $this->getPath(), \implode(', ', \array_map('json_encode', $this->values)))); + $ex->setPath($this->getPath()); + throw $ex; + } + return $value; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Exception/DuplicateKeyException.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Exception/DuplicateKeyException.php new file mode 100755 index 00000000..55cb5655 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Exception/DuplicateKeyException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception; + +/** + * This exception is thrown whenever the key of an array is not unique. This can + * only be the case if the configuration is coming from an XML file. + * + * @author Johannes M. Schmitt + */ +class DuplicateKeyException extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException +{ +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Exception/Exception.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Exception/Exception.php new file mode 100755 index 00000000..cd797a22 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Exception/Exception.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception; + +/** + * Base exception for all configuration exceptions. + * + * @author Johannes M. Schmitt + */ +class Exception extends \RuntimeException +{ +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Exception/ForbiddenOverwriteException.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Exception/ForbiddenOverwriteException.php new file mode 100755 index 00000000..22748fb3 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Exception/ForbiddenOverwriteException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception; + +/** + * This exception is thrown when a configuration path is overwritten from a + * subsequent configuration file, but the entry node specifically forbids this. + * + * @author Johannes M. Schmitt + */ +class ForbiddenOverwriteException extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException +{ +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Exception/InvalidConfigurationException.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Exception/InvalidConfigurationException.php new file mode 100755 index 00000000..343a129d --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Exception/InvalidConfigurationException.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception; + +/** + * A very general exception which can be thrown whenever non of the more specific + * exceptions is suitable. + * + * @author Johannes M. Schmitt + */ +class InvalidConfigurationException extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\Exception +{ + private $path; + private $containsHints = \false; + public function setPath($path) + { + $this->path = $path; + } + public function getPath() + { + return $this->path; + } + /** + * Adds extra information that is suffixed to the original exception message. + * + * @param string $hint + */ + public function addHint($hint) + { + if (!$this->containsHints) { + $this->message .= "\nHint: " . $hint; + $this->containsHints = \true; + } else { + $this->message .= ', ' . $hint; + } + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Exception/InvalidDefinitionException.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Exception/InvalidDefinitionException.php new file mode 100755 index 00000000..b7d36d97 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Exception/InvalidDefinitionException.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception; + +/** + * Thrown when an error is detected in a node Definition. + * + * @author Victor Berchet + */ +class InvalidDefinitionException extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\Exception +{ +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Exception/InvalidTypeException.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Exception/InvalidTypeException.php new file mode 100755 index 00000000..08dd336d --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Exception/InvalidTypeException.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception; + +/** + * This exception is thrown if an invalid type is encountered. + * + * @author Johannes M. Schmitt + */ +class InvalidTypeException extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException +{ +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Exception/UnsetKeyException.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Exception/UnsetKeyException.php new file mode 100755 index 00000000..9f49b1c4 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Exception/UnsetKeyException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception; + +/** + * This exception is usually not encountered by the end-user, but only used + * internally to signal the parent scope to unset a key. + * + * @author Johannes M. Schmitt + */ +class UnsetKeyException extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\Exception +{ +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/FloatNode.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/FloatNode.php new file mode 100755 index 00000000..020a03f3 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/FloatNode.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidTypeException; +/** + * This node represents a float value in the config tree. + * + * @author Jeanmonod David + */ +class FloatNode extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\NumericNode +{ + /** + * {@inheritdoc} + */ + protected function validateType($value) + { + // Integers are also accepted, we just cast them + if (\is_int($value)) { + $value = (float) $value; + } + if (!\is_float($value)) { + $ex = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidTypeException(\sprintf('Invalid type for path "%s". Expected float, but got %s.', $this->getPath(), \gettype($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + throw $ex; + } + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/IntegerNode.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/IntegerNode.php new file mode 100755 index 00000000..a047f28d --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/IntegerNode.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidTypeException; +/** + * This node represents an integer value in the config tree. + * + * @author Jeanmonod David + */ +class IntegerNode extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\NumericNode +{ + /** + * {@inheritdoc} + */ + protected function validateType($value) + { + if (!\is_int($value)) { + $ex = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidTypeException(\sprintf('Invalid type for path "%s". Expected int, but got %s.', $this->getPath(), \gettype($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + throw $ex; + } + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/NodeInterface.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/NodeInterface.php new file mode 100755 index 00000000..810de65a --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/NodeInterface.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidTypeException; +/** + * Common Interface among all nodes. + * + * In most cases, it is better to inherit from BaseNode instead of implementing + * this interface yourself. + * + * @author Johannes M. Schmitt + */ +interface NodeInterface +{ + /** + * Returns the name of the node. + * + * @return string The name of the node + */ + public function getName(); + /** + * Returns the path of the node. + * + * @return string The node path + */ + public function getPath(); + /** + * Returns true when the node is required. + * + * @return bool If the node is required + */ + public function isRequired(); + /** + * Returns true when the node has a default value. + * + * @return bool If the node has a default value + */ + public function hasDefaultValue(); + /** + * Returns the default value of the node. + * + * @return mixed The default value + * + * @throws \RuntimeException if the node has no default value + */ + public function getDefaultValue(); + /** + * Normalizes a value. + * + * @param mixed $value The value to normalize + * + * @return mixed The normalized value + * + * @throws InvalidTypeException if the value type is invalid + */ + public function normalize($value); + /** + * Merges two values together. + * + * @param mixed $leftSide + * @param mixed $rightSide + * + * @return mixed The merged value + * + * @throws ForbiddenOverwriteException if the configuration path cannot be overwritten + * @throws InvalidTypeException if the value type is invalid + */ + public function merge($leftSide, $rightSide); + /** + * Finalizes a value. + * + * @param mixed $value The value to finalize + * + * @return mixed The finalized value + * + * @throws InvalidTypeException if the value type is invalid + * @throws InvalidConfigurationException if the value is invalid configuration + */ + public function finalize($value); +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/NumericNode.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/NumericNode.php new file mode 100755 index 00000000..ae3b6233 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/NumericNode.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +/** + * This node represents a numeric value in the config tree. + * + * @author David Jeanmonod + */ +class NumericNode extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ScalarNode +{ + protected $min; + protected $max; + public function __construct($name, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\NodeInterface $parent = null, $min = null, $max = null) + { + parent::__construct($name, $parent); + $this->min = $min; + $this->max = $max; + } + /** + * {@inheritdoc} + */ + protected function finalizeValue($value) + { + $value = parent::finalizeValue($value); + $errorMsg = null; + if (isset($this->min) && $value < $this->min) { + $errorMsg = \sprintf('The value %s is too small for path "%s". Should be greater than or equal to %s', $value, $this->getPath(), $this->min); + } + if (isset($this->max) && $value > $this->max) { + $errorMsg = \sprintf('The value %s is too big for path "%s". Should be less than or equal to %s', $value, $this->getPath(), $this->max); + } + if (isset($errorMsg)) { + $ex = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException($errorMsg); + $ex->setPath($this->getPath()); + throw $ex; + } + return $value; + } + /** + * {@inheritdoc} + */ + protected function isValueEmpty($value) + { + // a numeric value cannot be empty + return \false; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/Processor.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Processor.php new file mode 100755 index 00000000..42726f68 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/Processor.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition; + +/** + * This class is the entry point for config normalization/merging/finalization. + * + * @author Johannes M. Schmitt + */ +class Processor +{ + /** + * Processes an array of configurations. + * + * @param NodeInterface $configTree The node tree describing the configuration + * @param array $configs An array of configuration items to process + * + * @return array The processed configuration + */ + public function process(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\NodeInterface $configTree, array $configs) + { + $currentConfig = []; + foreach ($configs as $config) { + $config = $configTree->normalize($config); + $currentConfig = $configTree->merge($currentConfig, $config); + } + return $configTree->finalize($currentConfig); + } + /** + * Processes an array of configurations. + * + * @param ConfigurationInterface $configuration The configuration class + * @param array $configs An array of configuration items to process + * + * @return array The processed configuration + */ + public function processConfiguration(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ConfigurationInterface $configuration, array $configs) + { + return $this->process($configuration->getConfigTreeBuilder()->buildTree(), $configs); + } + /** + * Normalizes a configuration entry. + * + * This method returns a normalize configuration array for a given key + * to remove the differences due to the original format (YAML and XML mainly). + * + * Here is an example. + * + * The configuration in XML: + * + * twig.extension.foo + * twig.extension.bar + * + * And the same configuration in YAML: + * + * extensions: ['twig.extension.foo', 'twig.extension.bar'] + * + * @param array $config A config array + * @param string $key The key to normalize + * @param string $plural The plural form of the key if it is irregular + * + * @return array + */ + public static function normalizeConfig($config, $key, $plural = null) + { + if (null === $plural) { + $plural = $key . 's'; + } + if (isset($config[$plural])) { + return $config[$plural]; + } + if (isset($config[$key])) { + if (\is_string($config[$key]) || !\is_int(\key($config[$key]))) { + // only one + return [$config[$key]]; + } + return $config[$key]; + } + return []; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/PrototypeNodeInterface.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/PrototypeNodeInterface.php new file mode 100755 index 00000000..6b333ed7 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/PrototypeNodeInterface.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition; + +/** + * This interface must be implemented by nodes which can be used as prototypes. + * + * @author Johannes M. Schmitt + */ +interface PrototypeNodeInterface extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\NodeInterface +{ + /** + * Sets the name of the node. + * + * @param string $name The name of the node + */ + public function setName($name); +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/PrototypedArrayNode.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/PrototypedArrayNode.php new file mode 100755 index 00000000..275e8d92 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/PrototypedArrayNode.php @@ -0,0 +1,337 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\DuplicateKeyException; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\Exception; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\UnsetKeyException; +/** + * Represents a prototyped Array node in the config tree. + * + * @author Johannes M. Schmitt + */ +class PrototypedArrayNode extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ArrayNode +{ + protected $prototype; + protected $keyAttribute; + protected $removeKeyAttribute = \false; + protected $minNumberOfElements = 0; + protected $defaultValue = []; + protected $defaultChildren; + /** + * @var NodeInterface[] An array of the prototypes of the simplified value children + */ + private $valuePrototypes = []; + /** + * Sets the minimum number of elements that a prototype based node must + * contain. By default this is zero, meaning no elements. + * + * @param int $number + */ + public function setMinNumberOfElements($number) + { + $this->minNumberOfElements = $number; + } + /** + * Sets the attribute which value is to be used as key. + * + * This is useful when you have an indexed array that should be an + * associative array. You can select an item from within the array + * to be the key of the particular item. For example, if "id" is the + * "key", then: + * + * [ + * ['id' => 'my_name', 'foo' => 'bar'], + * ]; + * + * becomes + * + * [ + * 'my_name' => ['foo' => 'bar'], + * ]; + * + * If you'd like "'id' => 'my_name'" to still be present in the resulting + * array, then you can set the second argument of this method to false. + * + * @param string $attribute The name of the attribute which value is to be used as a key + * @param bool $remove Whether or not to remove the key + */ + public function setKeyAttribute($attribute, $remove = \true) + { + $this->keyAttribute = $attribute; + $this->removeKeyAttribute = $remove; + } + /** + * Retrieves the name of the attribute which value should be used as key. + * + * @return string|null The name of the attribute + */ + public function getKeyAttribute() + { + return $this->keyAttribute; + } + /** + * Sets the default value of this node. + * + * @param string $value + * + * @throws \InvalidArgumentException if the default value is not an array + */ + public function setDefaultValue($value) + { + if (!\is_array($value)) { + throw new \InvalidArgumentException($this->getPath() . ': the default value of an array node has to be an array.'); + } + $this->defaultValue = $value; + } + /** + * {@inheritdoc} + */ + public function hasDefaultValue() + { + return \true; + } + /** + * Adds default children when none are set. + * + * @param int|string|array|null $children The number of children|The child name|The children names to be added + */ + public function setAddChildrenIfNoneSet($children = ['defaults']) + { + if (null === $children) { + $this->defaultChildren = ['defaults']; + } else { + $this->defaultChildren = \is_int($children) && $children > 0 ? \range(1, $children) : (array) $children; + } + } + /** + * {@inheritdoc} + * + * The default value could be either explicited or derived from the prototype + * default value. + */ + public function getDefaultValue() + { + if (null !== $this->defaultChildren) { + $default = $this->prototype->hasDefaultValue() ? $this->prototype->getDefaultValue() : []; + $defaults = []; + foreach (\array_values($this->defaultChildren) as $i => $name) { + $defaults[null === $this->keyAttribute ? $i : $name] = $default; + } + return $defaults; + } + return $this->defaultValue; + } + /** + * Sets the node prototype. + */ + public function setPrototype(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\PrototypeNodeInterface $node) + { + $this->prototype = $node; + } + /** + * Retrieves the prototype. + * + * @return PrototypeNodeInterface The prototype + */ + public function getPrototype() + { + return $this->prototype; + } + /** + * Disable adding concrete children for prototyped nodes. + * + * @throws Exception + */ + public function addChild(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\NodeInterface $node) + { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\Exception('A prototyped array node can not have concrete children.'); + } + /** + * Finalizes the value of this node. + * + * @param mixed $value + * + * @return mixed The finalized value + * + * @throws UnsetKeyException + * @throws InvalidConfigurationException if the node doesn't have enough children + */ + protected function finalizeValue($value) + { + if (\false === $value) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\UnsetKeyException(\sprintf('Unsetting key for path "%s", value: %s', $this->getPath(), \json_encode($value))); + } + foreach ($value as $k => $v) { + $prototype = $this->getPrototypeForChild($k); + try { + $value[$k] = $prototype->finalize($v); + } catch (\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\UnsetKeyException $e) { + unset($value[$k]); + } + } + if (\count($value) < $this->minNumberOfElements) { + $ex = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException(\sprintf('The path "%s" should have at least %d element(s) defined.', $this->getPath(), $this->minNumberOfElements)); + $ex->setPath($this->getPath()); + throw $ex; + } + return $value; + } + /** + * Normalizes the value. + * + * @param mixed $value The value to normalize + * + * @return mixed The normalized value + * + * @throws InvalidConfigurationException + * @throws DuplicateKeyException + */ + protected function normalizeValue($value) + { + if (\false === $value) { + return $value; + } + $value = $this->remapXml($value); + $isAssoc = \array_keys($value) !== \range(0, \count($value) - 1); + $normalized = []; + foreach ($value as $k => $v) { + if (null !== $this->keyAttribute && \is_array($v)) { + if (!isset($v[$this->keyAttribute]) && \is_int($k) && !$isAssoc) { + $ex = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException(\sprintf('The attribute "%s" must be set for path "%s".', $this->keyAttribute, $this->getPath())); + $ex->setPath($this->getPath()); + throw $ex; + } elseif (isset($v[$this->keyAttribute])) { + $k = $v[$this->keyAttribute]; + // remove the key attribute when required + if ($this->removeKeyAttribute) { + unset($v[$this->keyAttribute]); + } + // if only "value" is left + if (\array_keys($v) === ['value']) { + $v = $v['value']; + if ($this->prototype instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\ArrayNode && ($children = $this->prototype->getChildren()) && \array_key_exists('value', $children)) { + $valuePrototype = \current($this->valuePrototypes) ?: clone $children['value']; + $valuePrototype->parent = $this; + $originalClosures = $this->prototype->normalizationClosures; + if (\is_array($originalClosures)) { + $valuePrototypeClosures = $valuePrototype->normalizationClosures; + $valuePrototype->normalizationClosures = \is_array($valuePrototypeClosures) ? \array_merge($originalClosures, $valuePrototypeClosures) : $originalClosures; + } + $this->valuePrototypes[$k] = $valuePrototype; + } + } + } + if (\array_key_exists($k, $normalized)) { + $ex = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\DuplicateKeyException(\sprintf('Duplicate key "%s" for path "%s".', $k, $this->getPath())); + $ex->setPath($this->getPath()); + throw $ex; + } + } + $prototype = $this->getPrototypeForChild($k); + if (null !== $this->keyAttribute || $isAssoc) { + $normalized[$k] = $prototype->normalize($v); + } else { + $normalized[] = $prototype->normalize($v); + } + } + return $normalized; + } + /** + * Merges values together. + * + * @param mixed $leftSide The left side to merge + * @param mixed $rightSide The right side to merge + * + * @return mixed The merged values + * + * @throws InvalidConfigurationException + * @throws \RuntimeException + */ + protected function mergeValues($leftSide, $rightSide) + { + if (\false === $rightSide) { + // if this is still false after the last config has been merged the + // finalization pass will take care of removing this key entirely + return \false; + } + if (\false === $leftSide || !$this->performDeepMerging) { + return $rightSide; + } + foreach ($rightSide as $k => $v) { + // prototype, and key is irrelevant, append the element + if (null === $this->keyAttribute) { + $leftSide[] = $v; + continue; + } + // no conflict + if (!\array_key_exists($k, $leftSide)) { + if (!$this->allowNewKeys) { + $ex = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException(\sprintf('You are not allowed to define new elements for path "%s". Please define all elements for this path in one config file.', $this->getPath())); + $ex->setPath($this->getPath()); + throw $ex; + } + $leftSide[$k] = $v; + continue; + } + $prototype = $this->getPrototypeForChild($k); + $leftSide[$k] = $prototype->merge($leftSide[$k], $v); + } + return $leftSide; + } + /** + * Returns a prototype for the child node that is associated to $key in the value array. + * For general child nodes, this will be $this->prototype. + * But if $this->removeKeyAttribute is true and there are only two keys in the child node: + * one is same as this->keyAttribute and the other is 'value', then the prototype will be different. + * + * For example, assume $this->keyAttribute is 'name' and the value array is as follows: + * + * [ + * [ + * 'name' => 'name001', + * 'value' => 'value001' + * ] + * ] + * + * Now, the key is 0 and the child node is: + * + * [ + * 'name' => 'name001', + * 'value' => 'value001' + * ] + * + * When normalizing the value array, the 'name' element will removed from the child node + * and its value becomes the new key of the child node: + * + * [ + * 'name001' => ['value' => 'value001'] + * ] + * + * Now only 'value' element is left in the child node which can be further simplified into a string: + * + * ['name001' => 'value001'] + * + * Now, the key becomes 'name001' and the child node becomes 'value001' and + * the prototype of child node 'name001' should be a ScalarNode instead of an ArrayNode instance. + * + * @param string $key The key of the child node + * + * @return mixed The prototype instance + */ + private function getPrototypeForChild($key) + { + $prototype = isset($this->valuePrototypes[$key]) ? $this->valuePrototypes[$key] : $this->prototype; + $prototype->setName($key); + return $prototype; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/ScalarNode.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/ScalarNode.php new file mode 100755 index 00000000..2a58c401 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/ScalarNode.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidTypeException; +/** + * This node represents a scalar value in the config tree. + * + * The following values are considered scalars: + * * booleans + * * strings + * * null + * * integers + * * floats + * + * @author Johannes M. Schmitt + */ +class ScalarNode extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\VariableNode +{ + /** + * {@inheritdoc} + */ + protected function validateType($value) + { + if (!\is_scalar($value) && null !== $value) { + $ex = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidTypeException(\sprintf('Invalid type for path "%s". Expected scalar, but got %s.', $this->getPath(), \gettype($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + throw $ex; + } + } + /** + * {@inheritdoc} + */ + protected function isValueEmpty($value) + { + return null === $value || '' === $value; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Definition/VariableNode.php b/classes/Utilities/Misc/Symfony/Component/Config/Definition/VariableNode.php new file mode 100755 index 00000000..d751fa3e --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Definition/VariableNode.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +/** + * This node represents a value of variable type in the config tree. + * + * This node is intended for values of arbitrary type. + * Any PHP type is accepted as a value. + * + * @author Jeremy Mikola + */ +class VariableNode extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\BaseNode implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\PrototypeNodeInterface +{ + protected $defaultValueSet = \false; + protected $defaultValue; + protected $allowEmptyValue = \true; + public function setDefaultValue($value) + { + $this->defaultValueSet = \true; + $this->defaultValue = $value; + } + /** + * {@inheritdoc} + */ + public function hasDefaultValue() + { + return $this->defaultValueSet; + } + /** + * {@inheritdoc} + */ + public function getDefaultValue() + { + $v = $this->defaultValue; + return $v instanceof \Closure ? $v() : $v; + } + /** + * Sets if this node is allowed to have an empty value. + * + * @param bool $boolean True if this entity will accept empty values + */ + public function setAllowEmptyValue($boolean) + { + $this->allowEmptyValue = (bool) $boolean; + } + /** + * {@inheritdoc} + */ + public function setName($name) + { + $this->name = $name; + } + /** + * {@inheritdoc} + */ + protected function validateType($value) + { + } + /** + * {@inheritdoc} + */ + protected function finalizeValue($value) + { + if (!$this->allowEmptyValue && $this->isValueEmpty($value)) { + $ex = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException(\sprintf('The path "%s" cannot contain an empty value, but got %s.', $this->getPath(), \json_encode($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + throw $ex; + } + return $value; + } + /** + * {@inheritdoc} + */ + protected function normalizeValue($value) + { + return $value; + } + /** + * {@inheritdoc} + */ + protected function mergeValues($leftSide, $rightSide) + { + return $rightSide; + } + /** + * Evaluates if the given value is to be treated as empty. + * + * By default, PHP's empty() function is used to test for emptiness. This + * method may be overridden by subtypes to better match their understanding + * of empty data. + * + * @param mixed $value + * + * @return bool + */ + protected function isValueEmpty($value) + { + return empty($value); + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/DependencyInjection/ConfigCachePass.php b/classes/Utilities/Misc/Symfony/Component/Config/DependencyInjection/ConfigCachePass.php new file mode 100755 index 00000000..2f77cac8 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/DependencyInjection/ConfigCachePass.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\DependencyInjection; + +@\trigger_error(\sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use tagged iterator arguments instead.', \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\DependencyInjection\ConfigCachePass::class), \E_USER_DEPRECATED); +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\ContainerBuilder; +/** + * Adds services tagged config_cache.resource_checker to the config_cache_factory service, ordering them by priority. + * + * @author Matthias Pigulla + * @author Benjamin Klotz + * + * @deprecated since version 3.4, to be removed in 4.0. Use tagged iterator arguments instead. + */ +class ConfigCachePass implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface +{ + use PriorityTaggedServiceTrait; + private $factoryServiceId; + private $resourceCheckerTag; + public function __construct($factoryServiceId = 'config_cache_factory', $resourceCheckerTag = 'config_cache.resource_checker') + { + $this->factoryServiceId = $factoryServiceId; + $this->resourceCheckerTag = $resourceCheckerTag; + } + public function process(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\ContainerBuilder $container) + { + $resourceCheckers = $this->findAndSortTaggedServices($this->resourceCheckerTag, $container); + if (empty($resourceCheckers)) { + return; + } + $container->getDefinition($this->factoryServiceId)->replaceArgument(0, new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\Argument\IteratorArgument($resourceCheckers)); + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Exception/FileLoaderImportCircularReferenceException.php b/classes/Utilities/Misc/Symfony/Component/Config/Exception/FileLoaderImportCircularReferenceException.php new file mode 100755 index 00000000..bcb5642a --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Exception/FileLoaderImportCircularReferenceException.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Exception; + +/** + * Exception class for when a circular reference is detected when importing resources. + * + * @author Fabien Potencier + */ +class FileLoaderImportCircularReferenceException extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Exception\FileLoaderLoadException +{ + public function __construct(array $resources, $code = null, $previous = null) + { + $message = \sprintf('Circular reference detected in "%s" ("%s" > "%s").', $this->varToString($resources[0]), \implode('" > "', $resources), $resources[0]); + \Exception::__construct($message, $code, $previous); + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Exception/FileLoaderLoadException.php b/classes/Utilities/Misc/Symfony/Component/Config/Exception/FileLoaderLoadException.php new file mode 100755 index 00000000..c9dbad4e --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Exception/FileLoaderLoadException.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Exception; + +/** + * Exception class for when a resource cannot be loaded or imported. + * + * @author Ryan Weaver + */ +class FileLoaderLoadException extends \Exception +{ + /** + * @param string $resource The resource that could not be imported + * @param string $sourceResource The original resource importing the new resource + * @param int $code The error code + * @param \Exception $previous A previous exception + * @param string $type The type of resource + */ + public function __construct($resource, $sourceResource = null, $code = null, $previous = null, $type = null) + { + $message = ''; + if ($previous) { + // Include the previous exception, to help the user see what might be the underlying cause + // Trim the trailing period of the previous message. We only want 1 period remove so no rtrim... + if ('.' === \substr($previous->getMessage(), -1)) { + $trimmedMessage = \substr($previous->getMessage(), 0, -1); + $message .= \sprintf('%s', $trimmedMessage) . ' in '; + } else { + $message .= \sprintf('%s', $previous->getMessage()) . ' in '; + } + $message .= $resource . ' '; + // show tweaked trace to complete the human readable sentence + if (null === $sourceResource) { + $message .= \sprintf('(which is loaded in resource "%s")', $this->varToString($resource)); + } else { + $message .= \sprintf('(which is being imported from "%s")', $this->varToString($sourceResource)); + } + $message .= '.'; + // if there's no previous message, present it the default way + } elseif (null === $sourceResource) { + $message .= \sprintf('Cannot load resource "%s".', $this->varToString($resource)); + } else { + $message .= \sprintf('Cannot import resource "%s" from "%s".', $this->varToString($resource), $this->varToString($sourceResource)); + } + // Is the resource located inside a bundle? + if ('@' === $resource[0]) { + $parts = \explode(\DIRECTORY_SEPARATOR, $resource); + $bundle = \substr($parts[0], 1); + $message .= \sprintf(' Make sure the "%s" bundle is correctly registered and loaded in the application kernel class.', $bundle); + $message .= \sprintf(' If the bundle is registered, make sure the bundle path "%s" is not empty.', $resource); + } elseif (null !== $type) { + // maybe there is no loader for this specific type + if ('annotation' === $type) { + $message .= ' Make sure annotations are installed and enabled.'; + } else { + $message .= \sprintf(' Make sure there is a loader supporting the "%s" type.', $type); + } + } + parent::__construct($message, $code, $previous); + } + protected function varToString($var) + { + if (\is_object($var)) { + return \sprintf('Object(%s)', \get_class($var)); + } + if (\is_array($var)) { + $a = []; + foreach ($var as $k => $v) { + $a[] = \sprintf('%s => %s', $k, $this->varToString($v)); + } + return \sprintf('Array(%s)', \implode(', ', $a)); + } + if (\is_resource($var)) { + return \sprintf('Resource(%s)', \get_resource_type($var)); + } + if (null === $var) { + return 'null'; + } + if (\false === $var) { + return 'false'; + } + if (\true === $var) { + return 'true'; + } + return (string) $var; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Exception/FileLocatorFileNotFoundException.php b/classes/Utilities/Misc/Symfony/Component/Config/Exception/FileLocatorFileNotFoundException.php new file mode 100755 index 00000000..0a126333 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Exception/FileLocatorFileNotFoundException.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Exception; + +/** + * File locator exception if a file does not exist. + * + * @author Leo Feyer + */ +class FileLocatorFileNotFoundException extends \InvalidArgumentException +{ + private $paths; + public function __construct($message = '', $code = 0, $previous = null, array $paths = []) + { + parent::__construct($message, $code, $previous); + $this->paths = $paths; + } + public function getPaths() + { + return $this->paths; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/FileLocator.php b/classes/Utilities/Misc/Symfony/Component/Config/FileLocator.php new file mode 100755 index 00000000..48dec9e6 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/FileLocator.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; +/** + * FileLocator uses an array of pre-defined paths to find files. + * + * @author Fabien Potencier + */ +class FileLocator implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\FileLocatorInterface +{ + protected $paths; + /** + * @param string|array $paths A path or an array of paths where to look for resources + */ + public function __construct($paths = []) + { + $this->paths = (array) $paths; + } + /** + * {@inheritdoc} + */ + public function locate($name, $currentPath = null, $first = \true) + { + if ('' == $name) { + throw new \InvalidArgumentException('An empty file name is not valid to be located.'); + } + if ($this->isAbsolutePath($name)) { + if (!\file_exists($name)) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Exception\FileLocatorFileNotFoundException(\sprintf('The file "%s" does not exist.', $name), 0, null, [$name]); + } + return $name; + } + $paths = $this->paths; + if (null !== $currentPath) { + \array_unshift($paths, $currentPath); + } + $paths = \array_unique($paths); + $filepaths = $notfound = []; + foreach ($paths as $path) { + if (@\file_exists($file = $path . \DIRECTORY_SEPARATOR . $name)) { + if (\true === $first) { + return $file; + } + $filepaths[] = $file; + } else { + $notfound[] = $file; + } + } + if (!$filepaths) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Exception\FileLocatorFileNotFoundException(\sprintf('The file "%s" does not exist (in: %s).', $name, \implode(', ', $paths)), 0, null, $notfound); + } + return $filepaths; + } + /** + * Returns whether the file path is an absolute path. + * + * @param string $file A file path + * + * @return bool + */ + private function isAbsolutePath($file) + { + if ('/' === $file[0] || '\\' === $file[0] || \strlen($file) > 3 && \ctype_alpha($file[0]) && ':' === $file[1] && ('\\' === $file[2] || '/' === $file[2]) || null !== \parse_url($file, \PHP_URL_SCHEME)) { + return \true; + } + return \false; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/FileLocatorInterface.php b/classes/Utilities/Misc/Symfony/Component/Config/FileLocatorInterface.php new file mode 100755 index 00000000..561589d9 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/FileLocatorInterface.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; +/** + * @author Fabien Potencier + */ +interface FileLocatorInterface +{ + /** + * Returns a full path for a given file name. + * + * @param string $name The file name to locate + * @param string|null $currentPath The current path + * @param bool $first Whether to return the first occurrence or an array of filenames + * + * @return string|array The full path to the file or an array of file paths + * + * @throws \InvalidArgumentException If $name is empty + * @throws FileLocatorFileNotFoundException If a file is not found + */ + public function locate($name, $currentPath = null, $first = \true); +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Loader/DelegatingLoader.php b/classes/Utilities/Misc/Symfony/Component/Config/Loader/DelegatingLoader.php new file mode 100755 index 00000000..f7b2822a --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Loader/DelegatingLoader.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Loader; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Exception\FileLoaderLoadException; +/** + * DelegatingLoader delegates loading to other loaders using a loader resolver. + * + * This loader acts as an array of LoaderInterface objects - each having + * a chance to load a given resource (handled by the resolver) + * + * @author Fabien Potencier + */ +class DelegatingLoader extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Loader\Loader +{ + public function __construct(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Loader\LoaderResolverInterface $resolver) + { + $this->resolver = $resolver; + } + /** + * {@inheritdoc} + */ + public function load($resource, $type = null) + { + if (\false === ($loader = $this->resolver->resolve($resource, $type))) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Exception\FileLoaderLoadException($resource, null, null, null, $type); + } + return $loader->load($resource, $type); + } + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + return \false !== $this->resolver->resolve($resource, $type); + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Loader/FileLoader.php b/classes/Utilities/Misc/Symfony/Component/Config/Loader/FileLoader.php new file mode 100755 index 00000000..ad7ff69d --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Loader/FileLoader.php @@ -0,0 +1,152 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Loader; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Exception\FileLoaderLoadException; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\FileLocatorInterface; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\FileExistenceResource; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\GlobResource; +/** + * FileLoader is the abstract class used by all built-in loaders that are file based. + * + * @author Fabien Potencier + */ +abstract class FileLoader extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Loader\Loader +{ + protected static $loading = []; + protected $locator; + private $currentDir; + public function __construct(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\FileLocatorInterface $locator) + { + $this->locator = $locator; + } + /** + * Sets the current directory. + * + * @param string $dir + */ + public function setCurrentDir($dir) + { + $this->currentDir = $dir; + } + /** + * Returns the file locator used by this loader. + * + * @return FileLocatorInterface + */ + public function getLocator() + { + return $this->locator; + } + /** + * Imports a resource. + * + * @param mixed $resource A Resource + * @param string|null $type The resource type or null if unknown + * @param bool $ignoreErrors Whether to ignore import errors or not + * @param string|null $sourceResource The original resource importing the new resource + * + * @return mixed + * + * @throws FileLoaderLoadException + * @throws FileLoaderImportCircularReferenceException + * @throws FileLocatorFileNotFoundException + */ + public function import($resource, $type = null, $ignoreErrors = \false, $sourceResource = null) + { + if (\is_string($resource) && \strlen($resource) !== ($i = \strcspn($resource, '*?{['))) { + $ret = []; + $isSubpath = 0 !== $i && \false !== \strpos(\substr($resource, 0, $i), '/'); + foreach ($this->glob($resource, \false, $_, $ignoreErrors || !$isSubpath) as $path => $info) { + if (null !== ($res = $this->doImport($path, $type, $ignoreErrors, $sourceResource))) { + $ret[] = $res; + } + $isSubpath = \true; + } + if ($isSubpath) { + return isset($ret[1]) ? $ret : (isset($ret[0]) ? $ret[0] : null); + } + } + return $this->doImport($resource, $type, $ignoreErrors, $sourceResource); + } + /** + * @internal + */ + protected function glob($pattern, $recursive, &$resource = null, $ignoreErrors = \false) + { + if (\strlen($pattern) === ($i = \strcspn($pattern, '*?{['))) { + $prefix = $pattern; + $pattern = ''; + } elseif (0 === $i || \false === \strpos(\substr($pattern, 0, $i), '/')) { + $prefix = '.'; + $pattern = '/' . $pattern; + } else { + $prefix = \dirname(\substr($pattern, 0, 1 + $i)); + $pattern = \substr($pattern, \strlen($prefix)); + } + try { + $prefix = $this->locator->locate($prefix, $this->currentDir, \true); + } catch (\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Exception\FileLocatorFileNotFoundException $e) { + if (!$ignoreErrors) { + throw $e; + } + $resource = []; + foreach ($e->getPaths() as $path) { + $resource[] = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\FileExistenceResource($path); + } + return; + } + $resource = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\GlobResource($prefix, $pattern, $recursive); + foreach ($resource as $path => $info) { + (yield $path => $info); + } + } + private function doImport($resource, $type = null, $ignoreErrors = \false, $sourceResource = null) + { + try { + $loader = $this->resolve($resource, $type); + if ($loader instanceof self && null !== $this->currentDir) { + $resource = $loader->getLocator()->locate($resource, $this->currentDir, \false); + } + $resources = \is_array($resource) ? $resource : [$resource]; + for ($i = 0; $i < ($resourcesCount = \count($resources)); ++$i) { + if (isset(self::$loading[$resources[$i]])) { + if ($i == $resourcesCount - 1) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException(\array_keys(self::$loading)); + } + } else { + $resource = $resources[$i]; + break; + } + } + self::$loading[$resource] = \true; + try { + $ret = $loader->load($resource, $type); + } finally { + unset(self::$loading[$resource]); + } + return $ret; + } catch (\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException $e) { + throw $e; + } catch (\Exception $e) { + if (!$ignoreErrors) { + // prevent embedded imports from nesting multiple exceptions + if ($e instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Exception\FileLoaderLoadException) { + throw $e; + } + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Exception\FileLoaderLoadException($resource, $sourceResource, null, $e, $type); + } + } + return null; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Loader/GlobFileLoader.php b/classes/Utilities/Misc/Symfony/Component/Config/Loader/GlobFileLoader.php new file mode 100755 index 00000000..91c84daf --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Loader/GlobFileLoader.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Loader; + +/** + * GlobFileLoader loads files from a glob pattern. + * + * @author Fabien Potencier + */ +class GlobFileLoader extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Loader\FileLoader +{ + /** + * {@inheritdoc} + */ + public function load($resource, $type = null) + { + return $this->import($resource); + } + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + return 'glob' === $type; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Loader/Loader.php b/classes/Utilities/Misc/Symfony/Component/Config/Loader/Loader.php new file mode 100755 index 00000000..15c63af0 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Loader/Loader.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Loader; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Exception\FileLoaderLoadException; +/** + * Loader is the abstract class used by all built-in loaders. + * + * @author Fabien Potencier + */ +abstract class Loader implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Loader\LoaderInterface +{ + protected $resolver; + /** + * {@inheritdoc} + */ + public function getResolver() + { + return $this->resolver; + } + /** + * {@inheritdoc} + */ + public function setResolver(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Loader\LoaderResolverInterface $resolver) + { + $this->resolver = $resolver; + } + /** + * Imports a resource. + * + * @param mixed $resource A resource + * @param string|null $type The resource type or null if unknown + * + * @return mixed + */ + public function import($resource, $type = null) + { + return $this->resolve($resource, $type)->load($resource, $type); + } + /** + * Finds a loader able to load an imported resource. + * + * @param mixed $resource A resource + * @param string|null $type The resource type or null if unknown + * + * @return $this|LoaderInterface + * + * @throws FileLoaderLoadException If no loader is found + */ + public function resolve($resource, $type = null) + { + if ($this->supports($resource, $type)) { + return $this; + } + $loader = null === $this->resolver ? \false : $this->resolver->resolve($resource, $type); + if (\false === $loader) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Exception\FileLoaderLoadException($resource, null, null, null, $type); + } + return $loader; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Loader/LoaderInterface.php b/classes/Utilities/Misc/Symfony/Component/Config/Loader/LoaderInterface.php new file mode 100755 index 00000000..9ec4b6d1 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Loader/LoaderInterface.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Loader; + +/** + * LoaderInterface is the interface implemented by all loader classes. + * + * @author Fabien Potencier + */ +interface LoaderInterface +{ + /** + * Loads a resource. + * + * @param mixed $resource The resource + * @param string|null $type The resource type or null if unknown + * + * @throws \Exception If something went wrong + */ + public function load($resource, $type = null); + /** + * Returns whether this class supports the given resource. + * + * @param mixed $resource A resource + * @param string|null $type The resource type or null if unknown + * + * @return bool True if this class supports the given resource, false otherwise + */ + public function supports($resource, $type = null); + /** + * Gets the loader resolver. + * + * @return LoaderResolverInterface A LoaderResolverInterface instance + */ + public function getResolver(); + /** + * Sets the loader resolver. + */ + public function setResolver(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Loader\LoaderResolverInterface $resolver); +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Loader/LoaderResolver.php b/classes/Utilities/Misc/Symfony/Component/Config/Loader/LoaderResolver.php new file mode 100755 index 00000000..07562ca0 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Loader/LoaderResolver.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Loader; + +/** + * LoaderResolver selects a loader for a given resource. + * + * A resource can be anything (e.g. a full path to a config file or a Closure). + * Each loader determines whether it can load a resource and how. + * + * @author Fabien Potencier + */ +class LoaderResolver implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Loader\LoaderResolverInterface +{ + /** + * @var LoaderInterface[] An array of LoaderInterface objects + */ + private $loaders = []; + /** + * @param LoaderInterface[] $loaders An array of loaders + */ + public function __construct(array $loaders = []) + { + foreach ($loaders as $loader) { + $this->addLoader($loader); + } + } + /** + * {@inheritdoc} + */ + public function resolve($resource, $type = null) + { + foreach ($this->loaders as $loader) { + if ($loader->supports($resource, $type)) { + return $loader; + } + } + return \false; + } + public function addLoader(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Loader\LoaderInterface $loader) + { + $this->loaders[] = $loader; + $loader->setResolver($this); + } + /** + * Returns the registered loaders. + * + * @return LoaderInterface[] An array of LoaderInterface instances + */ + public function getLoaders() + { + return $this->loaders; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Loader/LoaderResolverInterface.php b/classes/Utilities/Misc/Symfony/Component/Config/Loader/LoaderResolverInterface.php new file mode 100755 index 00000000..36439005 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Loader/LoaderResolverInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Loader; + +/** + * LoaderResolverInterface selects a loader for a given resource. + * + * @author Fabien Potencier + */ +interface LoaderResolverInterface +{ + /** + * Returns a loader able to load the resource. + * + * @param mixed $resource A resource + * @param string|null $type The resource type or null if unknown + * + * @return LoaderInterface|false The loader or false if none is able to load the resource + */ + public function resolve($resource, $type = null); +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Resource/ClassExistenceResource.php b/classes/Utilities/Misc/Symfony/Component/Config/Resource/ClassExistenceResource.php new file mode 100755 index 00000000..3751ee68 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Resource/ClassExistenceResource.php @@ -0,0 +1,178 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource; + +/** + * ClassExistenceResource represents a class existence. + * Freshness is only evaluated against resource existence. + * + * The resource must be a fully-qualified class name. + * + * @author Fabien Potencier + */ +class ClassExistenceResource implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\SelfCheckingResourceInterface, \Serializable +{ + private $resource; + private $exists; + private static $autoloadLevel = 0; + private static $autoloadedClass; + private static $existsCache = []; + /** + * @param string $resource The fully-qualified class name + * @param bool|null $exists Boolean when the existency check has already been done + */ + public function __construct($resource, $exists = null) + { + $this->resource = $resource; + if (null !== $exists) { + $this->exists = (bool) $exists; + } + } + /** + * {@inheritdoc} + */ + public function __toString() + { + return $this->resource; + } + /** + * @return string The file path to the resource + */ + public function getResource() + { + return $this->resource; + } + /** + * {@inheritdoc} + * + * @throws \ReflectionException when a parent class/interface/trait is not found + */ + public function isFresh($timestamp) + { + $loaded = \class_exists($this->resource, \false) || \interface_exists($this->resource, \false) || \trait_exists($this->resource, \false); + if (null !== ($exists =& self::$existsCache[(int) (0 >= $timestamp)][$this->resource])) { + $exists = $exists || $loaded; + } elseif (!($exists = $loaded)) { + if (!self::$autoloadLevel++) { + \spl_autoload_register(__CLASS__ . '::throwOnRequiredClass'); + } + $autoloadedClass = self::$autoloadedClass; + self::$autoloadedClass = \ltrim($this->resource, '\\'); + try { + $exists = \class_exists($this->resource) || \interface_exists($this->resource, \false) || \trait_exists($this->resource, \false); + } catch (\Exception $e) { + try { + self::throwOnRequiredClass($this->resource, $e); + } catch (\ReflectionException $e) { + if (0 >= $timestamp) { + unset(self::$existsCache[1][$this->resource]); + throw $e; + } + } + } finally { + self::$autoloadedClass = $autoloadedClass; + if (!--self::$autoloadLevel) { + \spl_autoload_unregister(__CLASS__ . '::throwOnRequiredClass'); + } + } + } + if (null === $this->exists) { + $this->exists = $exists; + } + return $this->exists xor !$exists; + } + /** + * @internal + */ + public function serialize() + { + if (null === $this->exists) { + $this->isFresh(0); + } + return \serialize([$this->resource, $this->exists]); + } + /** + * @internal + */ + public function unserialize($serialized) + { + list($this->resource, $this->exists) = \unserialize($serialized); + } + /** + * Throws a reflection exception when the passed class does not exist but is required. + * + * A class is considered "not required" when it's loaded as part of a "class_exists" or similar check. + * + * This function can be used as an autoload function to throw a reflection + * exception if the class was not found by previous autoload functions. + * + * A previous exception can be passed. In this case, the class is considered as being + * required totally, so if it doesn't exist, a reflection exception is always thrown. + * If it exists, the previous exception is rethrown. + * + * @throws \ReflectionException + * + * @internal + */ + public static function throwOnRequiredClass($class, \Exception $previous = null) + { + // If the passed class is the resource being checked, we shouldn't throw. + if (null === $previous && self::$autoloadedClass === $class) { + return; + } + if (\class_exists($class, \false) || \interface_exists($class, \false) || \trait_exists($class, \false)) { + if (null !== $previous) { + throw $previous; + } + return; + } + if ($previous instanceof \ReflectionException) { + throw $previous; + } + $e = new \ReflectionException(\sprintf('Class "%s" not found while loading "%s".', $class, self::$autoloadedClass), 0, $previous); + if (null !== $previous) { + throw $e; + } + $trace = \debug_backtrace(); + $autoloadFrame = ['function' => 'spl_autoload_call', 'args' => [$class]]; + if (\false === ($i = \array_search($autoloadFrame, $trace, \true))) { + throw $e; + } + if (isset($trace[++$i]['function']) && !isset($trace[$i]['class'])) { + switch ($trace[$i]['function']) { + case 'get_class_methods': + case 'get_class_vars': + case 'get_parent_class': + case 'is_a': + case 'is_subclass_of': + case 'class_exists': + case 'class_implements': + case 'class_parents': + case 'trait_exists': + case 'defined': + case 'interface_exists': + case 'method_exists': + case 'property_exists': + case 'is_callable': + return; + } + $props = ['file' => isset($trace[$i]['file']) ? $trace[$i]['file'] : null, 'line' => isset($trace[$i]['line']) ? $trace[$i]['line'] : null, 'trace' => \array_slice($trace, 1 + $i)]; + foreach ($props as $p => $v) { + if (null !== $v) { + $r = new \ReflectionProperty('Exception', $p); + $r->setAccessible(\true); + $r->setValue($e, $v); + } + } + } + throw $e; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Resource/ComposerResource.php b/classes/Utilities/Misc/Symfony/Component/Config/Resource/ComposerResource.php new file mode 100755 index 00000000..fbb0272c --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Resource/ComposerResource.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource; + +/** + * ComposerResource tracks the PHP version and Composer dependencies. + * + * @author Nicolas Grekas + */ +class ComposerResource implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\SelfCheckingResourceInterface, \Serializable +{ + private $vendors; + private static $runtimeVendors; + public function __construct() + { + self::refresh(); + $this->vendors = self::$runtimeVendors; + } + public function getVendors() + { + return \array_keys($this->vendors); + } + /** + * {@inheritdoc} + */ + public function __toString() + { + return __CLASS__; + } + /** + * {@inheritdoc} + */ + public function isFresh($timestamp) + { + self::refresh(); + return \array_values(self::$runtimeVendors) === \array_values($this->vendors); + } + /** + * @internal + */ + public function serialize() + { + return \serialize($this->vendors); + } + /** + * @internal + */ + public function unserialize($serialized) + { + $this->vendors = \unserialize($serialized); + } + private static function refresh() + { + self::$runtimeVendors = []; + foreach (\get_declared_classes() as $class) { + if ('C' === $class[0] && 0 === \strpos($class, 'ComposerAutoloaderInit')) { + $r = new \ReflectionClass($class); + $v = \dirname(\dirname($r->getFileName())); + if (\file_exists($v . '/composer/installed.json')) { + self::$runtimeVendors[$v] = @\filemtime($v . '/composer/installed.json'); + } + } + } + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Resource/DirectoryResource.php b/classes/Utilities/Misc/Symfony/Component/Config/Resource/DirectoryResource.php new file mode 100755 index 00000000..4c5854e6 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Resource/DirectoryResource.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource; + +/** + * DirectoryResource represents a resources stored in a subdirectory tree. + * + * @author Fabien Potencier + */ +class DirectoryResource implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\SelfCheckingResourceInterface, \Serializable +{ + private $resource; + private $pattern; + /** + * @param string $resource The file path to the resource + * @param string|null $pattern A pattern to restrict monitored files + * + * @throws \InvalidArgumentException + */ + public function __construct($resource, $pattern = null) + { + $this->resource = \realpath($resource) ?: (\file_exists($resource) ? $resource : \false); + $this->pattern = $pattern; + if (\false === $this->resource || !\is_dir($this->resource)) { + throw new \InvalidArgumentException(\sprintf('The directory "%s" does not exist.', $resource)); + } + } + /** + * {@inheritdoc} + */ + public function __toString() + { + return \md5(\serialize([$this->resource, $this->pattern])); + } + /** + * @return string The file path to the resource + */ + public function getResource() + { + return $this->resource; + } + /** + * Returns the pattern to restrict monitored files. + * + * @return string|null + */ + public function getPattern() + { + return $this->pattern; + } + /** + * {@inheritdoc} + */ + public function isFresh($timestamp) + { + if (!\is_dir($this->resource)) { + return \false; + } + if ($timestamp < \filemtime($this->resource)) { + return \false; + } + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->resource), \RecursiveIteratorIterator::SELF_FIRST) as $file) { + // if regex filtering is enabled only check matching files + if ($this->pattern && $file->isFile() && !\preg_match($this->pattern, $file->getBasename())) { + continue; + } + // always monitor directories for changes, except the .. entries + // (otherwise deleted files wouldn't get detected) + if ($file->isDir() && '/..' === \substr($file, -3)) { + continue; + } + // for broken links + try { + $fileMTime = $file->getMTime(); + } catch (\RuntimeException $e) { + continue; + } + // early return if a file's mtime exceeds the passed timestamp + if ($timestamp < $fileMTime) { + return \false; + } + } + return \true; + } + /** + * @internal + */ + public function serialize() + { + return \serialize([$this->resource, $this->pattern]); + } + /** + * @internal + */ + public function unserialize($serialized) + { + list($this->resource, $this->pattern) = \unserialize($serialized); + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Resource/FileExistenceResource.php b/classes/Utilities/Misc/Symfony/Component/Config/Resource/FileExistenceResource.php new file mode 100755 index 00000000..ea7764c5 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Resource/FileExistenceResource.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource; + +/** + * FileExistenceResource represents a resource stored on the filesystem. + * Freshness is only evaluated against resource creation or deletion. + * + * The resource can be a file or a directory. + * + * @author Charles-Henri Bruyand + */ +class FileExistenceResource implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\SelfCheckingResourceInterface, \Serializable +{ + private $resource; + private $exists; + /** + * @param string $resource The file path to the resource + */ + public function __construct($resource) + { + $this->resource = (string) $resource; + $this->exists = \file_exists($resource); + } + /** + * {@inheritdoc} + */ + public function __toString() + { + return $this->resource; + } + /** + * @return string The file path to the resource + */ + public function getResource() + { + return $this->resource; + } + /** + * {@inheritdoc} + */ + public function isFresh($timestamp) + { + return \file_exists($this->resource) === $this->exists; + } + /** + * @internal + */ + public function serialize() + { + return \serialize([$this->resource, $this->exists]); + } + /** + * @internal + */ + public function unserialize($serialized) + { + list($this->resource, $this->exists) = \unserialize($serialized); + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Resource/FileResource.php b/classes/Utilities/Misc/Symfony/Component/Config/Resource/FileResource.php new file mode 100755 index 00000000..ae44c276 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Resource/FileResource.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource; + +/** + * FileResource represents a resource stored on the filesystem. + * + * The resource can be a file or a directory. + * + * @author Fabien Potencier + */ +class FileResource implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\SelfCheckingResourceInterface, \Serializable +{ + /** + * @var string|false + */ + private $resource; + /** + * @param string $resource The file path to the resource + * + * @throws \InvalidArgumentException + */ + public function __construct($resource) + { + $this->resource = \realpath($resource) ?: (\file_exists($resource) ? $resource : \false); + if (\false === $this->resource) { + throw new \InvalidArgumentException(\sprintf('The file "%s" does not exist.', $resource)); + } + } + /** + * {@inheritdoc} + */ + public function __toString() + { + return $this->resource; + } + /** + * @return string The canonicalized, absolute path to the resource + */ + public function getResource() + { + return $this->resource; + } + /** + * {@inheritdoc} + */ + public function isFresh($timestamp) + { + return \false !== ($filemtime = @\filemtime($this->resource)) && $filemtime <= $timestamp; + } + /** + * @internal + */ + public function serialize() + { + return \serialize($this->resource); + } + /** + * @internal + */ + public function unserialize($serialized) + { + $this->resource = \unserialize($serialized); + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Resource/GlobResource.php b/classes/Utilities/Misc/Symfony/Component/Config/Resource/GlobResource.php new file mode 100755 index 00000000..c8cd085d --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Resource/GlobResource.php @@ -0,0 +1,133 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Finder\Finder; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Finder\Glob; +/** + * GlobResource represents a set of resources stored on the filesystem. + * + * Only existence/removal is tracked (not mtimes.) + * + * @author Nicolas Grekas + */ +class GlobResource implements \IteratorAggregate, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\SelfCheckingResourceInterface, \Serializable +{ + private $prefix; + private $pattern; + private $recursive; + private $hash; + /** + * @param string $prefix A directory prefix + * @param string $pattern A glob pattern + * @param bool $recursive Whether directories should be scanned recursively or not + * + * @throws \InvalidArgumentException + */ + public function __construct($prefix, $pattern, $recursive) + { + $this->prefix = \realpath($prefix) ?: (\file_exists($prefix) ? $prefix : \false); + $this->pattern = $pattern; + $this->recursive = $recursive; + if (\false === $this->prefix) { + throw new \InvalidArgumentException(\sprintf('The path "%s" does not exist.', $prefix)); + } + } + public function getPrefix() + { + return $this->prefix; + } + /** + * {@inheritdoc} + */ + public function __toString() + { + return 'glob.' . $this->prefix . $this->pattern . (int) $this->recursive; + } + /** + * {@inheritdoc} + */ + public function isFresh($timestamp) + { + $hash = $this->computeHash(); + if (null === $this->hash) { + $this->hash = $hash; + } + return $this->hash === $hash; + } + /** + * @internal + */ + public function serialize() + { + if (null === $this->hash) { + $this->hash = $this->computeHash(); + } + return \serialize([$this->prefix, $this->pattern, $this->recursive, $this->hash]); + } + /** + * @internal + */ + public function unserialize($serialized) + { + list($this->prefix, $this->pattern, $this->recursive, $this->hash) = \unserialize($serialized); + } + public function getIterator() + { + if (!\file_exists($this->prefix) || !$this->recursive && '' === $this->pattern) { + return; + } + if (0 !== \strpos($this->prefix, 'phar://') && \false === \strpos($this->pattern, '/**/') && (\defined('GLOB_BRACE') || \false === \strpos($this->pattern, '{'))) { + $paths = \glob($this->prefix . $this->pattern, \GLOB_NOSORT | (\defined('GLOB_BRACE') ? \GLOB_BRACE : 0)); + \sort($paths); + foreach ($paths as $path) { + if ($this->recursive && \is_dir($path)) { + $files = \iterator_to_array(new \RecursiveIteratorIterator(new \RecursiveCallbackFilterIterator(new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS), function (\SplFileInfo $file) { + return '.' !== $file->getBasename()[0]; + }), \RecursiveIteratorIterator::LEAVES_ONLY)); + \uasort($files, function (\SplFileInfo $a, \SplFileInfo $b) { + return (string) $a > (string) $b ? 1 : -1; + }); + foreach ($files as $path => $info) { + if ($info->isFile()) { + (yield $path => $info); + } + } + } elseif (\is_file($path)) { + (yield $path => new \SplFileInfo($path)); + } + } + return; + } + if (!\class_exists(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Finder\Finder::class)) { + throw new \LogicException(\sprintf('Extended glob pattern "%s" cannot be used as the Finder component is not installed.', $this->pattern)); + } + $finder = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Finder\Finder(); + $regex = \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Finder\Glob::toRegex($this->pattern); + if ($this->recursive) { + $regex = \substr_replace($regex, '(/|$)', -2, 1); + } + $prefixLen = \strlen($this->prefix); + foreach ($finder->followLinks()->sortByName()->in($this->prefix) as $path => $info) { + if (\preg_match($regex, \substr('\\' === \DIRECTORY_SEPARATOR ? \str_replace('\\', '/', $path) : $path, $prefixLen)) && $info->isFile()) { + (yield $path => $info); + } + } + } + private function computeHash() + { + $hash = \hash_init('md5'); + foreach ($this->getIterator() as $path => $info) { + \hash_update($hash, $path . "\n"); + } + return \hash_final($hash); + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Resource/ReflectionClassResource.php b/classes/Utilities/Misc/Symfony/Component/Config/Resource/ReflectionClassResource.php new file mode 100755 index 00000000..45ca6f98 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Resource/ReflectionClassResource.php @@ -0,0 +1,204 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\EventDispatcher\EventSubscriberInterface; +/** + * @author Nicolas Grekas + */ +class ReflectionClassResource implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\SelfCheckingResourceInterface, \Serializable +{ + private $files = []; + private $className; + private $classReflector; + private $excludedVendors = []; + private $hash; + public function __construct(\ReflectionClass $classReflector, $excludedVendors = []) + { + $this->className = $classReflector->name; + $this->classReflector = $classReflector; + $this->excludedVendors = $excludedVendors; + } + public function isFresh($timestamp) + { + if (null === $this->hash) { + $this->hash = $this->computeHash(); + $this->loadFiles($this->classReflector); + } + foreach ($this->files as $file => $v) { + if (\false === ($filemtime = @\filemtime($file))) { + return \false; + } + if ($filemtime > $timestamp) { + return $this->hash === $this->computeHash(); + } + } + return \true; + } + public function __toString() + { + return 'reflection.' . $this->className; + } + /** + * @internal + */ + public function serialize() + { + if (null === $this->hash) { + $this->hash = $this->computeHash(); + $this->loadFiles($this->classReflector); + } + return \serialize([$this->files, $this->className, $this->hash]); + } + /** + * @internal + */ + public function unserialize($serialized) + { + list($this->files, $this->className, $this->hash) = \unserialize($serialized); + } + private function loadFiles(\ReflectionClass $class) + { + foreach ($class->getInterfaces() as $v) { + $this->loadFiles($v); + } + do { + $file = $class->getFileName(); + if (\false !== $file && \file_exists($file)) { + foreach ($this->excludedVendors as $vendor) { + if (0 === \strpos($file, $vendor) && \false !== \strpbrk(\substr($file, \strlen($vendor), 1), '/' . \DIRECTORY_SEPARATOR)) { + $file = \false; + break; + } + } + if ($file) { + $this->files[$file] = null; + } + } + foreach ($class->getTraits() as $v) { + $this->loadFiles($v); + } + } while ($class = $class->getParentClass()); + } + private function computeHash() + { + if (null === $this->classReflector) { + try { + $this->classReflector = new \ReflectionClass($this->className); + } catch (\ReflectionException $e) { + // the class does not exist anymore + return \false; + } + } + $hash = \hash_init('md5'); + foreach ($this->generateSignature($this->classReflector) as $info) { + \hash_update($hash, $info); + } + return \hash_final($hash); + } + private function generateSignature(\ReflectionClass $class) + { + (yield $class->getDocComment()); + (yield (int) $class->isFinal()); + (yield (int) $class->isAbstract()); + if ($class->isTrait()) { + (yield \print_r(\class_uses($class->name), \true)); + } else { + (yield \print_r(\class_parents($class->name), \true)); + (yield \print_r(\class_implements($class->name), \true)); + (yield \print_r($class->getConstants(), \true)); + } + if (!$class->isInterface()) { + $defaults = $class->getDefaultProperties(); + foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED) as $p) { + (yield $p->getDocComment() . $p); + (yield \print_r(isset($defaults[$p->name]) && !\is_object($defaults[$p->name]) ? $defaults[$p->name] : null, \true)); + } + } + if (\defined('HHVM_VERSION')) { + foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) { + // workaround HHVM bug with variadics, see https://github.com/facebook/hhvm/issues/5762 + (yield \preg_replace('/^ @@.*/m', '', new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\ReflectionMethodHhvmWrapper($m->class, $m->name))); + } + } else { + foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) { + $defaults = []; + $parametersWithUndefinedConstants = []; + foreach ($m->getParameters() as $p) { + if (!$p->isDefaultValueAvailable()) { + $defaults[$p->name] = null; + continue; + } + if (!$p->isDefaultValueConstant() || \defined($p->getDefaultValueConstantName())) { + $defaults[$p->name] = $p->getDefaultValue(); + continue; + } + $defaults[$p->name] = $p->getDefaultValueConstantName(); + $parametersWithUndefinedConstants[$p->name] = \true; + } + if (!$parametersWithUndefinedConstants) { + (yield \preg_replace('/^ @@.*/m', '', $m)); + } else { + $stack = [$m->getDocComment(), $m->getName(), $m->isAbstract(), $m->isFinal(), $m->isStatic(), $m->isPublic(), $m->isPrivate(), $m->isProtected(), $m->returnsReference(), \PHP_VERSION_ID >= 70000 && $m->hasReturnType() ? \PHP_VERSION_ID >= 70100 ? $m->getReturnType()->getName() : (string) $m->getReturnType() : '']; + foreach ($m->getParameters() as $p) { + if (!isset($parametersWithUndefinedConstants[$p->name])) { + $stack[] = (string) $p; + } else { + $stack[] = $p->isOptional(); + $stack[] = \PHP_VERSION_ID >= 70000 && $p->hasType() ? \PHP_VERSION_ID >= 70100 ? $p->getType()->getName() : (string) $p->getType() : ''; + $stack[] = $p->isPassedByReference(); + $stack[] = \PHP_VERSION_ID >= 50600 ? $p->isVariadic() : ''; + $stack[] = $p->getName(); + } + } + (yield \implode(',', $stack)); + } + (yield \print_r($defaults, \true)); + } + } + if ($class->isAbstract() || $class->isInterface() || $class->isTrait()) { + return; + } + if (\interface_exists(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\EventDispatcher\EventSubscriberInterface::class, \false) && $class->isSubclassOf(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\EventDispatcher\EventSubscriberInterface::class)) { + (yield \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\EventDispatcher\EventSubscriberInterface::class); + (yield \print_r(\call_user_func([$class->name, 'getSubscribedEvents']), \true)); + } + if (\interface_exists(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\ServiceSubscriberInterface::class, \false) && $class->isSubclassOf(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\ServiceSubscriberInterface::class)) { + (yield \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\ServiceSubscriberInterface::class); + (yield \print_r(\call_user_func([$class->name, 'getSubscribedServices']), \true)); + } + } +} +/** + * @internal + */ +class ReflectionMethodHhvmWrapper extends \ReflectionMethod +{ + public function getParameters() + { + $params = []; + foreach (parent::getParameters() as $i => $p) { + $params[] = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\ReflectionParameterHhvmWrapper([$this->class, $this->name], $i); + } + return $params; + } +} +/** + * @internal + */ +class ReflectionParameterHhvmWrapper extends \ReflectionParameter +{ + public function getDefaultValue() + { + return [$this->isVariadic(), $this->isDefaultValueAvailable() ? parent::getDefaultValue() : null]; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Resource/ResourceInterface.php b/classes/Utilities/Misc/Symfony/Component/Config/Resource/ResourceInterface.php new file mode 100755 index 00000000..5a83be99 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Resource/ResourceInterface.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource; + +/** + * ResourceInterface is the interface that must be implemented by all Resource classes. + * + * @author Fabien Potencier + */ +interface ResourceInterface +{ + /** + * Returns a string representation of the Resource. + * + * This method is necessary to allow for resource de-duplication, for example by means + * of array_unique(). The string returned need not have a particular meaning, but has + * to be identical for different ResourceInterface instances referring to the same + * resource; and it should be unlikely to collide with that of other, unrelated + * resource instances. + * + * @return string A string representation unique to the underlying Resource + */ + public function __toString(); +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Resource/SelfCheckingResourceChecker.php b/classes/Utilities/Misc/Symfony/Component/Config/Resource/SelfCheckingResourceChecker.php new file mode 100755 index 00000000..7035b69a --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Resource/SelfCheckingResourceChecker.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\ResourceCheckerInterface; +/** + * Resource checker for instances of SelfCheckingResourceInterface. + * + * As these resources perform the actual check themselves, we can provide + * this class as a standard way of validating them. + * + * @author Matthias Pigulla + */ +class SelfCheckingResourceChecker implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\ResourceCheckerInterface +{ + public function supports(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\ResourceInterface $metadata) + { + return $metadata instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\SelfCheckingResourceInterface; + } + public function isFresh(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\ResourceInterface $resource, $timestamp) + { + /* @var SelfCheckingResourceInterface $resource */ + return $resource->isFresh($timestamp); + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Resource/SelfCheckingResourceInterface.php b/classes/Utilities/Misc/Symfony/Component/Config/Resource/SelfCheckingResourceInterface.php new file mode 100755 index 00000000..c7637e47 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Resource/SelfCheckingResourceInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource; + +/** + * Interface for Resources that can check for freshness autonomously, + * without special support from external services. + * + * @author Matthias Pigulla + */ +interface SelfCheckingResourceInterface extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\ResourceInterface +{ + /** + * Returns true if the resource has not been updated since the given timestamp. + * + * @param int $timestamp The last time the resource was loaded + * + * @return bool True if the resource has not been updated, false otherwise + */ + public function isFresh($timestamp); +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/ResourceCheckerConfigCache.php b/classes/Utilities/Misc/Symfony/Component/Config/ResourceCheckerConfigCache.php new file mode 100755 index 00000000..fee30d06 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/ResourceCheckerConfigCache.php @@ -0,0 +1,163 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\ResourceInterface; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Filesystem; +/** + * ResourceCheckerConfigCache uses instances of ResourceCheckerInterface + * to check whether cached data is still fresh. + * + * @author Matthias Pigulla + */ +class ResourceCheckerConfigCache implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\ConfigCacheInterface +{ + /** + * @var string + */ + private $file; + /** + * @var iterable|ResourceCheckerInterface[] + */ + private $resourceCheckers; + /** + * @param string $file The absolute cache path + * @param iterable|ResourceCheckerInterface[] $resourceCheckers The ResourceCheckers to use for the freshness check + */ + public function __construct($file, $resourceCheckers = []) + { + $this->file = $file; + $this->resourceCheckers = $resourceCheckers; + } + /** + * {@inheritdoc} + */ + public function getPath() + { + return $this->file; + } + /** + * Checks if the cache is still fresh. + * + * This implementation will make a decision solely based on the ResourceCheckers + * passed in the constructor. + * + * The first ResourceChecker that supports a given resource is considered authoritative. + * Resources with no matching ResourceChecker will silently be ignored and considered fresh. + * + * @return bool true if the cache is fresh, false otherwise + */ + public function isFresh() + { + if (!\is_file($this->file)) { + return \false; + } + if ($this->resourceCheckers instanceof \Traversable && !$this->resourceCheckers instanceof \Countable) { + $this->resourceCheckers = \iterator_to_array($this->resourceCheckers); + } + if (!\count($this->resourceCheckers)) { + return \true; + // shortcut - if we don't have any checkers we don't need to bother with the meta file at all + } + $metadata = $this->getMetaFile(); + if (!\is_file($metadata)) { + return \false; + } + $meta = $this->safelyUnserialize($metadata); + if (\false === $meta) { + return \false; + } + $time = \filemtime($this->file); + foreach ($meta as $resource) { + /* @var ResourceInterface $resource */ + foreach ($this->resourceCheckers as $checker) { + if (!$checker->supports($resource)) { + continue; + // next checker + } + if ($checker->isFresh($resource, $time)) { + break; + // no need to further check this resource + } + return \false; + // cache is stale + } + // no suitable checker found, ignore this resource + } + return \true; + } + /** + * Writes cache. + * + * @param string $content The content to write in the cache + * @param ResourceInterface[] $metadata An array of metadata + * + * @throws \RuntimeException When cache file can't be written + */ + public function write($content, array $metadata = null) + { + $mode = 0666; + $umask = \umask(); + $filesystem = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Filesystem(); + $filesystem->dumpFile($this->file, $content); + try { + $filesystem->chmod($this->file, $mode, $umask); + } catch (\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException $e) { + // discard chmod failure (some filesystem may not support it) + } + if (null !== $metadata) { + $filesystem->dumpFile($this->getMetaFile(), \serialize($metadata)); + try { + $filesystem->chmod($this->getMetaFile(), $mode, $umask); + } catch (\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException $e) { + // discard chmod failure (some filesystem may not support it) + } + } + if (\function_exists('opcache_invalidate') && \filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN)) { + @\opcache_invalidate($this->file, \true); + } + } + /** + * Gets the meta file path. + * + * @return string The meta file path + */ + private function getMetaFile() + { + return $this->file . '.meta'; + } + private function safelyUnserialize($file) + { + $e = null; + $meta = \false; + $content = \file_get_contents($file); + $signalingException = new \UnexpectedValueException(); + $prevUnserializeHandler = \ini_set('unserialize_callback_func', ''); + $prevErrorHandler = \set_error_handler(function ($type, $msg, $file, $line, $context = []) use(&$prevErrorHandler, $signalingException) { + if (__FILE__ === $file) { + throw $signalingException; + } + return $prevErrorHandler ? $prevErrorHandler($type, $msg, $file, $line, $context) : \false; + }); + try { + $meta = \unserialize($content); + } catch (\Error $e) { + } catch (\Exception $e) { + } + \restore_error_handler(); + \ini_set('unserialize_callback_func', $prevUnserializeHandler); + if (null !== $e && $e !== $signalingException) { + throw $e; + } + return $meta; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/ResourceCheckerConfigCacheFactory.php b/classes/Utilities/Misc/Symfony/Component/Config/ResourceCheckerConfigCacheFactory.php new file mode 100755 index 00000000..cc3cf770 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/ResourceCheckerConfigCacheFactory.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config; + +/** + * A ConfigCacheFactory implementation that validates the + * cache with an arbitrary set of ResourceCheckers. + * + * @author Matthias Pigulla + */ +class ResourceCheckerConfigCacheFactory implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\ConfigCacheFactoryInterface +{ + private $resourceCheckers = []; + /** + * @param iterable|ResourceCheckerInterface[] $resourceCheckers + */ + public function __construct($resourceCheckers = []) + { + $this->resourceCheckers = $resourceCheckers; + } + /** + * {@inheritdoc} + */ + public function cache($file, $callback) + { + if (!\is_callable($callback)) { + throw new \InvalidArgumentException(\sprintf('Invalid type for callback argument. Expected callable, but got "%s".', \gettype($callback))); + } + $cache = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\ResourceCheckerConfigCache($file, $this->resourceCheckers); + if (!$cache->isFresh()) { + \call_user_func($callback, $cache); + } + return $cache; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/ResourceCheckerInterface.php b/classes/Utilities/Misc/Symfony/Component/Config/ResourceCheckerInterface.php new file mode 100755 index 00000000..90551650 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/ResourceCheckerInterface.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\ResourceInterface; +/** + * Interface for ResourceCheckers. + * + * When a ResourceCheckerConfigCache instance is checked for freshness, all its associated + * metadata resources are passed to ResourceCheckers. The ResourceCheckers + * can then inspect the resources and decide whether the cache can be considered + * fresh or not. + * + * @author Matthias Pigulla + * @author Benjamin Klotz + */ +interface ResourceCheckerInterface +{ + /** + * Queries the ResourceChecker whether it can validate a given + * resource or not. + * + * @param ResourceInterface $metadata The resource to be checked for freshness + * + * @return bool True if the ResourceChecker can handle this resource type, false if not + */ + public function supports(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\ResourceInterface $metadata); + /** + * Validates the resource. + * + * @param ResourceInterface $resource The resource to be validated + * @param int $timestamp The timestamp at which the cache associated with this resource was created + * + * @return bool True if the resource has not changed since the given timestamp, false otherwise + */ + public function isFresh(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\ResourceInterface $resource, $timestamp); +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Util/Exception/InvalidXmlException.php b/classes/Utilities/Misc/Symfony/Component/Config/Util/Exception/InvalidXmlException.php new file mode 100755 index 00000000..4608b1ca --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Util/Exception/InvalidXmlException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Util\Exception; + +/** + * Exception class for when XML parsing with an XSD schema file path or a callable validator produces errors unrelated + * to the actual XML parsing. + * + * @author Ole Rößner + */ +class InvalidXmlException extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Util\Exception\XmlParsingException +{ +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Util/Exception/XmlParsingException.php b/classes/Utilities/Misc/Symfony/Component/Config/Util/Exception/XmlParsingException.php new file mode 100755 index 00000000..533cf8dd --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Util/Exception/XmlParsingException.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Util\Exception; + +/** + * Exception class for when XML cannot be parsed properly. + * + * @author Ole Rößner + */ +class XmlParsingException extends \InvalidArgumentException +{ +} diff --git a/classes/Utilities/Misc/Symfony/Component/Config/Util/XmlUtils.php b/classes/Utilities/Misc/Symfony/Component/Config/Util/XmlUtils.php new file mode 100755 index 00000000..94fd9e09 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Config/Util/XmlUtils.php @@ -0,0 +1,232 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Util; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Util\Exception\InvalidXmlException; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Util\Exception\XmlParsingException; +/** + * XMLUtils is a bunch of utility methods to XML operations. + * + * This class contains static methods only and is not meant to be instantiated. + * + * @author Fabien Potencier + * @author Martin Hasoň + * @author Ole Rößner + */ +class XmlUtils +{ + /** + * This class should not be instantiated. + */ + private function __construct() + { + } + /** + * Parses an XML string. + * + * @param string $content An XML string + * @param string|callable|null $schemaOrCallable An XSD schema file path, a callable, or null to disable validation + * + * @return \DOMDocument + * + * @throws XmlParsingException When parsing of XML file returns error + * @throws InvalidXmlException When parsing of XML with schema or callable produces any errors unrelated to the XML parsing itself + * @throws \RuntimeException When DOM extension is missing + */ + public static function parse($content, $schemaOrCallable = null) + { + if (!\extension_loaded('dom')) { + throw new \RuntimeException('Extension DOM is required.'); + } + $internalErrors = \libxml_use_internal_errors(\true); + $disableEntities = \libxml_disable_entity_loader(\true); + \libxml_clear_errors(); + $dom = new \DOMDocument(); + $dom->validateOnParse = \true; + if (!$dom->loadXML($content, \LIBXML_NONET | (\defined('LIBXML_COMPACT') ? \LIBXML_COMPACT : 0))) { + \libxml_disable_entity_loader($disableEntities); + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Util\Exception\XmlParsingException(\implode("\n", static::getXmlErrors($internalErrors))); + } + $dom->normalizeDocument(); + \libxml_use_internal_errors($internalErrors); + \libxml_disable_entity_loader($disableEntities); + foreach ($dom->childNodes as $child) { + if (\XML_DOCUMENT_TYPE_NODE === $child->nodeType) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Util\Exception\XmlParsingException('Document types are not allowed.'); + } + } + if (null !== $schemaOrCallable) { + $internalErrors = \libxml_use_internal_errors(\true); + \libxml_clear_errors(); + $e = null; + if (\is_callable($schemaOrCallable)) { + try { + $valid = \call_user_func($schemaOrCallable, $dom, $internalErrors); + } catch (\Exception $e) { + $valid = \false; + } + } elseif (!\is_array($schemaOrCallable) && \is_file((string) $schemaOrCallable)) { + $schemaSource = \file_get_contents((string) $schemaOrCallable); + $valid = @$dom->schemaValidateSource($schemaSource); + } else { + \libxml_use_internal_errors($internalErrors); + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Util\Exception\XmlParsingException('The schemaOrCallable argument has to be a valid path to XSD file or callable.'); + } + if (!$valid) { + $messages = static::getXmlErrors($internalErrors); + if (empty($messages)) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Util\Exception\InvalidXmlException('The XML is not valid.', 0, $e); + } + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Util\Exception\XmlParsingException(\implode("\n", $messages), 0, $e); + } + } + \libxml_clear_errors(); + \libxml_use_internal_errors($internalErrors); + return $dom; + } + /** + * Loads an XML file. + * + * @param string $file An XML file path + * @param string|callable|null $schemaOrCallable An XSD schema file path, a callable, or null to disable validation + * + * @return \DOMDocument + * + * @throws \InvalidArgumentException When loading of XML file returns error + * @throws XmlParsingException When XML parsing returns any errors + * @throws \RuntimeException When DOM extension is missing + */ + public static function loadFile($file, $schemaOrCallable = null) + { + $content = @\file_get_contents($file); + if ('' === \trim($content)) { + throw new \InvalidArgumentException(\sprintf('File %s does not contain valid XML, it is empty.', $file)); + } + try { + return static::parse($content, $schemaOrCallable); + } catch (\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Util\Exception\InvalidXmlException $e) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Util\Exception\XmlParsingException(\sprintf('The XML file "%s" is not valid.', $file), 0, $e->getPrevious()); + } + } + /** + * Converts a \DOMElement object to a PHP array. + * + * The following rules applies during the conversion: + * + * * Each tag is converted to a key value or an array + * if there is more than one "value" + * + * * The content of a tag is set under a "value" key (bar) + * if the tag also has some nested tags + * + * * The attributes are converted to keys () + * + * * The nested-tags are converted to keys (bar) + * + * @param \DOMElement $element A \DOMElement instance + * @param bool $checkPrefix Check prefix in an element or an attribute name + * + * @return mixed + */ + public static function convertDomElementToArray(\DOMElement $element, $checkPrefix = \true) + { + $prefix = (string) $element->prefix; + $empty = \true; + $config = []; + foreach ($element->attributes as $name => $node) { + if ($checkPrefix && !\in_array((string) $node->prefix, ['', $prefix], \true)) { + continue; + } + $config[$name] = static::phpize($node->value); + $empty = \false; + } + $nodeValue = \false; + foreach ($element->childNodes as $node) { + if ($node instanceof \DOMText) { + if ('' !== \trim($node->nodeValue)) { + $nodeValue = \trim($node->nodeValue); + $empty = \false; + } + } elseif ($checkPrefix && $prefix != (string) $node->prefix) { + continue; + } elseif (!$node instanceof \DOMComment) { + $value = static::convertDomElementToArray($node, $checkPrefix); + $key = $node->localName; + if (isset($config[$key])) { + if (!\is_array($config[$key]) || !\is_int(\key($config[$key]))) { + $config[$key] = [$config[$key]]; + } + $config[$key][] = $value; + } else { + $config[$key] = $value; + } + $empty = \false; + } + } + if (\false !== $nodeValue) { + $value = static::phpize($nodeValue); + if (\count($config)) { + $config['value'] = $value; + } else { + $config = $value; + } + } + return !$empty ? $config : null; + } + /** + * Converts an xml value to a PHP type. + * + * @param mixed $value + * + * @return mixed + */ + public static function phpize($value) + { + $value = (string) $value; + $lowercaseValue = \strtolower($value); + switch (\true) { + case 'null' === $lowercaseValue: + return null; + case \ctype_digit($value): + $raw = $value; + $cast = (int) $value; + return '0' == $value[0] ? \octdec($value) : ((string) $raw === (string) $cast ? $cast : $raw); + case isset($value[1]) && '-' === $value[0] && \ctype_digit(\substr($value, 1)): + $raw = $value; + $cast = (int) $value; + return '0' == $value[1] ? \octdec($value) : ((string) $raw === (string) $cast ? $cast : $raw); + case 'true' === $lowercaseValue: + return \true; + case 'false' === $lowercaseValue: + return \false; + case isset($value[1]) && '0b' == $value[0] . $value[1] && \preg_match('/^0b[01]*$/', $value): + return \bindec($value); + case \is_numeric($value): + return '0x' === $value[0] . $value[1] ? \hexdec($value) : (float) $value; + case \preg_match('/^0x[0-9a-f]++$/i', $value): + return \hexdec($value); + case \preg_match('/^[+-]?[0-9]+(\\.[0-9]+)?$/', $value): + return (float) $value; + default: + return $value; + } + } + protected static function getXmlErrors($internalErrors) + { + $errors = []; + foreach (\libxml_get_errors() as $error) { + $errors[] = \sprintf('[%s %s] %s (in %s - line %d, column %d)', \LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR', $error->code, \trim($error->message), $error->file ?: 'n/a', $error->line, $error->column); + } + \libxml_clear_errors(); + \libxml_use_internal_errors($internalErrors); + return $errors; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Filesystem/Exception/ExceptionInterface.php b/classes/Utilities/Misc/Symfony/Component/Filesystem/Exception/ExceptionInterface.php new file mode 100755 index 00000000..452edc6a --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Filesystem/Exception/ExceptionInterface.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception; + +/** + * Exception interface for all exceptions thrown by the component. + * + * @author Romain Neutron + */ +interface ExceptionInterface +{ +} diff --git a/classes/Utilities/Misc/Symfony/Component/Filesystem/Exception/FileNotFoundException.php b/classes/Utilities/Misc/Symfony/Component/Filesystem/Exception/FileNotFoundException.php new file mode 100755 index 00000000..c5226c75 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Filesystem/Exception/FileNotFoundException.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception; + +/** + * Exception class thrown when a file couldn't be found. + * + * @author Fabien Potencier + * @author Christian Gärtner + */ +class FileNotFoundException extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException +{ + public function __construct($message = null, $code = 0, \Exception $previous = null, $path = null) + { + if (null === $message) { + if (null === $path) { + $message = 'File could not be found.'; + } else { + $message = \sprintf('File "%s" could not be found.', $path); + } + } + parent::__construct($message, $code, $previous, $path); + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Filesystem/Exception/IOException.php b/classes/Utilities/Misc/Symfony/Component/Filesystem/Exception/IOException.php new file mode 100755 index 00000000..857c25f1 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Filesystem/Exception/IOException.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception; + +/** + * Exception class thrown when a filesystem operation failure happens. + * + * @author Romain Neutron + * @author Christian Gärtner + * @author Fabien Potencier + */ +class IOException extends \RuntimeException implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOExceptionInterface +{ + private $path; + public function __construct($message, $code = 0, \Exception $previous = null, $path = null) + { + $this->path = $path; + parent::__construct($message, $code, $previous); + } + /** + * {@inheritdoc} + */ + public function getPath() + { + return $this->path; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Filesystem/Exception/IOExceptionInterface.php b/classes/Utilities/Misc/Symfony/Component/Filesystem/Exception/IOExceptionInterface.php new file mode 100755 index 00000000..6dbec7ef --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Filesystem/Exception/IOExceptionInterface.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception; + +/** + * IOException interface for file and input/output stream related exceptions thrown by the component. + * + * @author Christian Gärtner + */ +interface IOExceptionInterface extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\ExceptionInterface +{ + /** + * Returns the associated path for the exception. + * + * @return string|null The path + */ + public function getPath(); +} diff --git a/classes/Utilities/Misc/Symfony/Component/Filesystem/Filesystem.php b/classes/Utilities/Misc/Symfony/Component/Filesystem/Filesystem.php new file mode 100755 index 00000000..19460d3c --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Filesystem/Filesystem.php @@ -0,0 +1,670 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\FileNotFoundException; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException; +/** + * Provides basic utility to manipulate the file system. + * + * @author Fabien Potencier + */ +class Filesystem +{ + private static $lastError; + /** + * Copies a file. + * + * If the target file is older than the origin file, it's always overwritten. + * If the target file is newer, it is overwritten only when the + * $overwriteNewerFiles option is set to true. + * + * @param string $originFile The original filename + * @param string $targetFile The target filename + * @param bool $overwriteNewerFiles If true, target files newer than origin files are overwritten + * + * @throws FileNotFoundException When originFile doesn't exist + * @throws IOException When copy fails + */ + public function copy($originFile, $targetFile, $overwriteNewerFiles = \false) + { + $originIsLocal = \stream_is_local($originFile) || 0 === \stripos($originFile, 'file://'); + if ($originIsLocal && !\is_file($originFile)) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\FileNotFoundException(\sprintf('Failed to copy "%s" because file does not exist.', $originFile), 0, null, $originFile); + } + $this->mkdir(\dirname($targetFile)); + $doCopy = \true; + if (!$overwriteNewerFiles && null === \parse_url($originFile, \PHP_URL_HOST) && \is_file($targetFile)) { + $doCopy = \filemtime($originFile) > \filemtime($targetFile); + } + if ($doCopy) { + // https://bugs.php.net/64634 + if (\false === ($source = @\fopen($originFile, 'r'))) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Failed to copy "%s" to "%s" because source file could not be opened for reading.', $originFile, $targetFile), 0, null, $originFile); + } + // Stream context created to allow files overwrite when using FTP stream wrapper - disabled by default + if (\false === ($target = @\fopen($targetFile, 'w', null, \stream_context_create(['ftp' => ['overwrite' => \true]])))) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Failed to copy "%s" to "%s" because target file could not be opened for writing.', $originFile, $targetFile), 0, null, $originFile); + } + $bytesCopied = \stream_copy_to_stream($source, $target); + \fclose($source); + \fclose($target); + unset($source, $target); + if (!\is_file($targetFile)) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Failed to copy "%s" to "%s".', $originFile, $targetFile), 0, null, $originFile); + } + if ($originIsLocal) { + // Like `cp`, preserve executable permission bits + @\chmod($targetFile, \fileperms($targetFile) | \fileperms($originFile) & 0111); + if ($bytesCopied !== ($bytesOrigin = \filesize($originFile))) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Failed to copy the whole content of "%s" to "%s" (%g of %g bytes copied).', $originFile, $targetFile, $bytesCopied, $bytesOrigin), 0, null, $originFile); + } + } + } + } + /** + * Creates a directory recursively. + * + * @param string|iterable $dirs The directory path + * @param int $mode The directory mode + * + * @throws IOException On any directory creation failure + */ + public function mkdir($dirs, $mode = 0777) + { + foreach ($this->toIterable($dirs) as $dir) { + if (\is_dir($dir)) { + continue; + } + if (!self::box('mkdir', $dir, $mode, \true)) { + if (!\is_dir($dir)) { + // The directory was not created by a concurrent process. Let's throw an exception with a developer friendly error message if we have one + if (self::$lastError) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Failed to create "%s": %s.', $dir, self::$lastError), 0, null, $dir); + } + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Failed to create "%s"', $dir), 0, null, $dir); + } + } + } + } + /** + * Checks the existence of files or directories. + * + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to check + * + * @return bool true if the file exists, false otherwise + */ + public function exists($files) + { + $maxPathLength = \PHP_MAXPATHLEN - 2; + foreach ($this->toIterable($files) as $file) { + if (\strlen($file) > $maxPathLength) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Could not check if file exist because path length exceeds %d characters.', $maxPathLength), 0, null, $file); + } + if (!\file_exists($file)) { + return \false; + } + } + return \true; + } + /** + * Sets access and modification time of file. + * + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to create + * @param int|null $time The touch time as a Unix timestamp, if not supplied the current system time is used + * @param int|null $atime The access time as a Unix timestamp, if not supplied the current system time is used + * + * @throws IOException When touch fails + */ + public function touch($files, $time = null, $atime = null) + { + foreach ($this->toIterable($files) as $file) { + $touch = $time ? @\touch($file, $time, $atime) : @\touch($file); + if (\true !== $touch) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Failed to touch "%s".', $file), 0, null, $file); + } + } + } + /** + * Removes files or directories. + * + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to remove + * + * @throws IOException When removal fails + */ + public function remove($files) + { + if ($files instanceof \Traversable) { + $files = \iterator_to_array($files, \false); + } elseif (!\is_array($files)) { + $files = [$files]; + } + $files = \array_reverse($files); + foreach ($files as $file) { + if (\is_link($file)) { + // See https://bugs.php.net/52176 + if (!(self::box('unlink', $file) || '\\' !== \DIRECTORY_SEPARATOR || self::box('rmdir', $file)) && \file_exists($file)) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Failed to remove symlink "%s": %s.', $file, self::$lastError)); + } + } elseif (\is_dir($file)) { + $this->remove(new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS)); + if (!self::box('rmdir', $file) && \file_exists($file)) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Failed to remove directory "%s": %s.', $file, self::$lastError)); + } + } elseif (!self::box('unlink', $file) && \file_exists($file)) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Failed to remove file "%s": %s.', $file, self::$lastError)); + } + } + } + /** + * Change mode for an array of files or directories. + * + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to change mode + * @param int $mode The new mode (octal) + * @param int $umask The mode mask (octal) + * @param bool $recursive Whether change the mod recursively or not + * + * @throws IOException When the change fails + */ + public function chmod($files, $mode, $umask = 00, $recursive = \false) + { + foreach ($this->toIterable($files) as $file) { + if (\true !== @\chmod($file, $mode & ~$umask)) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Failed to chmod file "%s".', $file), 0, null, $file); + } + if ($recursive && \is_dir($file) && !\is_link($file)) { + $this->chmod(new \FilesystemIterator($file), $mode, $umask, \true); + } + } + } + /** + * Change the owner of an array of files or directories. + * + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to change owner + * @param string $user The new owner user name + * @param bool $recursive Whether change the owner recursively or not + * + * @throws IOException When the change fails + */ + public function chown($files, $user, $recursive = \false) + { + foreach ($this->toIterable($files) as $file) { + if ($recursive && \is_dir($file) && !\is_link($file)) { + $this->chown(new \FilesystemIterator($file), $user, \true); + } + if (\is_link($file) && \function_exists('lchown')) { + if (\true !== @\lchown($file, $user)) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Failed to chown file "%s".', $file), 0, null, $file); + } + } else { + if (\true !== @\chown($file, $user)) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Failed to chown file "%s".', $file), 0, null, $file); + } + } + } + } + /** + * Change the group of an array of files or directories. + * + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to change group + * @param string $group The group name + * @param bool $recursive Whether change the group recursively or not + * + * @throws IOException When the change fails + */ + public function chgrp($files, $group, $recursive = \false) + { + foreach ($this->toIterable($files) as $file) { + if ($recursive && \is_dir($file) && !\is_link($file)) { + $this->chgrp(new \FilesystemIterator($file), $group, \true); + } + if (\is_link($file) && \function_exists('lchgrp')) { + if (\true !== @\lchgrp($file, $group) || \defined('HHVM_VERSION') && !\posix_getgrnam($group)) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Failed to chgrp file "%s".', $file), 0, null, $file); + } + } else { + if (\true !== @\chgrp($file, $group)) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Failed to chgrp file "%s".', $file), 0, null, $file); + } + } + } + } + /** + * Renames a file or a directory. + * + * @param string $origin The origin filename or directory + * @param string $target The new filename or directory + * @param bool $overwrite Whether to overwrite the target if it already exists + * + * @throws IOException When target file or directory already exists + * @throws IOException When origin cannot be renamed + */ + public function rename($origin, $target, $overwrite = \false) + { + // we check that target does not exist + if (!$overwrite && $this->isReadable($target)) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Cannot rename because the target "%s" already exists.', $target), 0, null, $target); + } + if (\true !== @\rename($origin, $target)) { + if (\is_dir($origin)) { + // See https://bugs.php.net/54097 & https://php.net/rename#113943 + $this->mirror($origin, $target, null, ['override' => $overwrite, 'delete' => $overwrite]); + $this->remove($origin); + return; + } + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Cannot rename "%s" to "%s".', $origin, $target), 0, null, $target); + } + } + /** + * Tells whether a file exists and is readable. + * + * @param string $filename Path to the file + * + * @return bool + * + * @throws IOException When windows path is longer than 258 characters + */ + private function isReadable($filename) + { + $maxPathLength = \PHP_MAXPATHLEN - 2; + if (\strlen($filename) > $maxPathLength) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Could not check if file is readable because path length exceeds %d characters.', $maxPathLength), 0, null, $filename); + } + return \is_readable($filename); + } + /** + * Creates a symbolic link or copy a directory. + * + * @param string $originDir The origin directory path + * @param string $targetDir The symbolic link name + * @param bool $copyOnWindows Whether to copy files if on Windows + * + * @throws IOException When symlink fails + */ + public function symlink($originDir, $targetDir, $copyOnWindows = \false) + { + if ('\\' === \DIRECTORY_SEPARATOR) { + $originDir = \strtr($originDir, '/', '\\'); + $targetDir = \strtr($targetDir, '/', '\\'); + if ($copyOnWindows) { + $this->mirror($originDir, $targetDir); + return; + } + } + $this->mkdir(\dirname($targetDir)); + if (\is_link($targetDir)) { + if (\readlink($targetDir) === $originDir) { + return; + } + $this->remove($targetDir); + } + if (!self::box('symlink', $originDir, $targetDir)) { + $this->linkException($originDir, $targetDir, 'symbolic'); + } + } + /** + * Creates a hard link, or several hard links to a file. + * + * @param string $originFile The original file + * @param string|string[] $targetFiles The target file(s) + * + * @throws FileNotFoundException When original file is missing or not a file + * @throws IOException When link fails, including if link already exists + */ + public function hardlink($originFile, $targetFiles) + { + if (!$this->exists($originFile)) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\FileNotFoundException(null, 0, null, $originFile); + } + if (!\is_file($originFile)) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\FileNotFoundException(\sprintf('Origin file "%s" is not a file', $originFile)); + } + foreach ($this->toIterable($targetFiles) as $targetFile) { + if (\is_file($targetFile)) { + if (\fileinode($originFile) === \fileinode($targetFile)) { + continue; + } + $this->remove($targetFile); + } + if (!self::box('link', $originFile, $targetFile)) { + $this->linkException($originFile, $targetFile, 'hard'); + } + } + } + /** + * @param string $origin + * @param string $target + * @param string $linkType Name of the link type, typically 'symbolic' or 'hard' + */ + private function linkException($origin, $target, $linkType) + { + if (self::$lastError) { + if ('\\' === \DIRECTORY_SEPARATOR && \false !== \strpos(self::$lastError, 'error code(1314)')) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Unable to create %s link due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?', $linkType), 0, null, $target); + } + } + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Failed to create %s link from "%s" to "%s".', $linkType, $origin, $target), 0, null, $target); + } + /** + * Resolves links in paths. + * + * With $canonicalize = false (default) + * - if $path does not exist or is not a link, returns null + * - if $path is a link, returns the next direct target of the link without considering the existence of the target + * + * With $canonicalize = true + * - if $path does not exist, returns null + * - if $path exists, returns its absolute fully resolved final version + * + * @param string $path A filesystem path + * @param bool $canonicalize Whether or not to return a canonicalized path + * + * @return string|null + */ + public function readlink($path, $canonicalize = \false) + { + if (!$canonicalize && !\is_link($path)) { + return null; + } + if ($canonicalize) { + if (!$this->exists($path)) { + return null; + } + if ('\\' === \DIRECTORY_SEPARATOR) { + $path = \readlink($path); + } + return \realpath($path); + } + if ('\\' === \DIRECTORY_SEPARATOR) { + return \realpath($path); + } + return \readlink($path); + } + /** + * Given an existing path, convert it to a path relative to a given starting path. + * + * @param string $endPath Absolute path of target + * @param string $startPath Absolute path where traversal begins + * + * @return string Path of target relative to starting path + */ + public function makePathRelative($endPath, $startPath) + { + if (!$this->isAbsolutePath($endPath) || !$this->isAbsolutePath($startPath)) { + @\trigger_error(\sprintf('Support for passing relative paths to %s() is deprecated since Symfony 3.4 and will be removed in 4.0.', __METHOD__), \E_USER_DEPRECATED); + } + // Normalize separators on Windows + if ('\\' === \DIRECTORY_SEPARATOR) { + $endPath = \str_replace('\\', '/', $endPath); + $startPath = \str_replace('\\', '/', $startPath); + } + $stripDriveLetter = function ($path) { + if (\strlen($path) > 2 && ':' === $path[1] && '/' === $path[2] && \ctype_alpha($path[0])) { + return \substr($path, 2); + } + return $path; + }; + $endPath = $stripDriveLetter($endPath); + $startPath = $stripDriveLetter($startPath); + // Split the paths into arrays + $startPathArr = \explode('/', \trim($startPath, '/')); + $endPathArr = \explode('/', \trim($endPath, '/')); + $normalizePathArray = function ($pathSegments, $absolute) { + $result = []; + foreach ($pathSegments as $segment) { + if ('..' === $segment && ($absolute || \count($result))) { + \array_pop($result); + } elseif ('.' !== $segment) { + $result[] = $segment; + } + } + return $result; + }; + $startPathArr = $normalizePathArray($startPathArr, static::isAbsolutePath($startPath)); + $endPathArr = $normalizePathArray($endPathArr, static::isAbsolutePath($endPath)); + // Find for which directory the common path stops + $index = 0; + while (isset($startPathArr[$index]) && isset($endPathArr[$index]) && $startPathArr[$index] === $endPathArr[$index]) { + ++$index; + } + // Determine how deep the start path is relative to the common path (ie, "web/bundles" = 2 levels) + if (1 === \count($startPathArr) && '' === $startPathArr[0]) { + $depth = 0; + } else { + $depth = \count($startPathArr) - $index; + } + // Repeated "../" for each level need to reach the common path + $traverser = \str_repeat('../', $depth); + $endPathRemainder = \implode('/', \array_slice($endPathArr, $index)); + // Construct $endPath from traversing to the common path, then to the remaining $endPath + $relativePath = $traverser . ('' !== $endPathRemainder ? $endPathRemainder . '/' : ''); + return '' === $relativePath ? './' : $relativePath; + } + /** + * Mirrors a directory to another. + * + * Copies files and directories from the origin directory into the target directory. By default: + * + * - existing files in the target directory will be overwritten, except if they are newer (see the `override` option) + * - files in the target directory that do not exist in the source directory will not be deleted (see the `delete` option) + * + * @param string $originDir The origin directory + * @param string $targetDir The target directory + * @param \Traversable|null $iterator Iterator that filters which files and directories to copy, if null a recursive iterator is created + * @param array $options An array of boolean options + * Valid options are: + * - $options['override'] If true, target files newer than origin files are overwritten (see copy(), defaults to false) + * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink(), defaults to false) + * - $options['delete'] Whether to delete files that are not in the source directory (defaults to false) + * + * @throws IOException When file type is unknown + */ + public function mirror($originDir, $targetDir, \Traversable $iterator = null, $options = []) + { + $targetDir = \rtrim($targetDir, '/\\'); + $originDir = \rtrim($originDir, '/\\'); + $originDirLen = \strlen($originDir); + // Iterate in destination folder to remove obsolete entries + if ($this->exists($targetDir) && isset($options['delete']) && $options['delete']) { + $deleteIterator = $iterator; + if (null === $deleteIterator) { + $flags = \FilesystemIterator::SKIP_DOTS; + $deleteIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($targetDir, $flags), \RecursiveIteratorIterator::CHILD_FIRST); + } + $targetDirLen = \strlen($targetDir); + foreach ($deleteIterator as $file) { + $origin = $originDir . \substr($file->getPathname(), $targetDirLen); + if (!$this->exists($origin)) { + $this->remove($file); + } + } + } + $copyOnWindows = \false; + if (isset($options['copy_on_windows'])) { + $copyOnWindows = $options['copy_on_windows']; + } + if (null === $iterator) { + $flags = $copyOnWindows ? \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS : \FilesystemIterator::SKIP_DOTS; + $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($originDir, $flags), \RecursiveIteratorIterator::SELF_FIRST); + } + if ($this->exists($originDir)) { + $this->mkdir($targetDir); + } + foreach ($iterator as $file) { + $target = $targetDir . \substr($file->getPathname(), $originDirLen); + if ($copyOnWindows) { + if (\is_file($file)) { + $this->copy($file, $target, isset($options['override']) ? $options['override'] : \false); + } elseif (\is_dir($file)) { + $this->mkdir($target); + } else { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Unable to guess "%s" file type.', $file), 0, null, $file); + } + } else { + if (\is_link($file)) { + $this->symlink($file->getLinkTarget(), $target); + } elseif (\is_dir($file)) { + $this->mkdir($target); + } elseif (\is_file($file)) { + $this->copy($file, $target, isset($options['override']) ? $options['override'] : \false); + } else { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Unable to guess "%s" file type.', $file), 0, null, $file); + } + } + } + } + /** + * Returns whether the file path is an absolute path. + * + * @param string $file A file path + * + * @return bool + */ + public function isAbsolutePath($file) + { + return \strspn($file, '/\\', 0, 1) || \strlen($file) > 3 && \ctype_alpha($file[0]) && ':' === $file[1] && \strspn($file, '/\\', 2, 1) || null !== \parse_url($file, \PHP_URL_SCHEME); + } + /** + * Creates a temporary file with support for custom stream wrappers. + * + * @param string $dir The directory where the temporary filename will be created + * @param string $prefix The prefix of the generated temporary filename + * Note: Windows uses only the first three characters of prefix + * + * @return string The new temporary filename (with path), or throw an exception on failure + */ + public function tempnam($dir, $prefix) + { + list($scheme, $hierarchy) = $this->getSchemeAndHierarchy($dir); + // If no scheme or scheme is "file" or "gs" (Google Cloud) create temp file in local filesystem + if (null === $scheme || 'file' === $scheme || 'gs' === $scheme) { + $tmpFile = @\tempnam($hierarchy, $prefix); + // If tempnam failed or no scheme return the filename otherwise prepend the scheme + if (\false !== $tmpFile) { + if (null !== $scheme && 'gs' !== $scheme) { + return $scheme . '://' . $tmpFile; + } + return $tmpFile; + } + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException('A temporary file could not be created.'); + } + // Loop until we create a valid temp file or have reached 10 attempts + for ($i = 0; $i < 10; ++$i) { + // Create a unique filename + $tmpFile = $dir . '/' . $prefix . \uniqid(\mt_rand(), \true); + // Use fopen instead of file_exists as some streams do not support stat + // Use mode 'x+' to atomically check existence and create to avoid a TOCTOU vulnerability + $handle = @\fopen($tmpFile, 'x+'); + // If unsuccessful restart the loop + if (\false === $handle) { + continue; + } + // Close the file if it was successfully opened + @\fclose($handle); + return $tmpFile; + } + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException('A temporary file could not be created.'); + } + /** + * Atomically dumps content into a file. + * + * @param string $filename The file to be written to + * @param string $content The data to write into the file + * + * @throws IOException if the file cannot be written to + */ + public function dumpFile($filename, $content) + { + $dir = \dirname($filename); + if (!\is_dir($dir)) { + $this->mkdir($dir); + } + if (!\is_writable($dir)) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Unable to write to the "%s" directory.', $dir), 0, null, $dir); + } + // Will create a temp file with 0600 access rights + // when the filesystem supports chmod. + $tmpFile = $this->tempnam($dir, \basename($filename)); + if (\false === @\file_put_contents($tmpFile, $content)) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Failed to write file "%s".', $filename), 0, null, $filename); + } + @\chmod($tmpFile, \file_exists($filename) ? \fileperms($filename) : 0666 & ~\umask()); + $this->rename($tmpFile, $filename, \true); + } + /** + * Appends content to an existing file. + * + * @param string $filename The file to which to append content + * @param string $content The content to append + * + * @throws IOException If the file is not writable + */ + public function appendToFile($filename, $content) + { + $dir = \dirname($filename); + if (!\is_dir($dir)) { + $this->mkdir($dir); + } + if (!\is_writable($dir)) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Unable to write to the "%s" directory.', $dir), 0, null, $dir); + } + if (\false === @\file_put_contents($filename, $content, \FILE_APPEND)) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('Failed to write file "%s".', $filename), 0, null, $filename); + } + } + /** + * @param mixed $files + * + * @return array|\Traversable + */ + private function toIterable($files) + { + return \is_array($files) || $files instanceof \Traversable ? $files : [$files]; + } + /** + * Gets a 2-tuple of scheme (may be null) and hierarchical part of a filename (e.g. file:///tmp -> [file, tmp]). + * + * @param string $filename The filename to be parsed + * + * @return array The filename scheme and hierarchical part + */ + private function getSchemeAndHierarchy($filename) + { + $components = \explode('://', $filename, 2); + return 2 === \count($components) ? [$components[0], $components[1]] : [null, $components[0]]; + } + /** + * @param callable $func + * + * @return mixed + */ + private static function box($func) + { + self::$lastError = null; + \set_error_handler(__CLASS__ . '::handleError'); + try { + $result = \call_user_func_array($func, \array_slice(\func_get_args(), 1)); + \restore_error_handler(); + return $result; + } catch (\Throwable $e) { + } catch (\Exception $e) { + } + \restore_error_handler(); + throw $e; + } + /** + * @internal + */ + public static function handleError($type, $msg) + { + self::$lastError = $msg; + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Filesystem/LockHandler.php b/classes/Utilities/Misc/Symfony/Component/Filesystem/LockHandler.php new file mode 100755 index 00000000..b97250ab --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Filesystem/LockHandler.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem; + +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Lock\Store\FlockStore; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Lock\Store\SemaphoreStore; +@\trigger_error(\sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use %s or %s instead.', \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\LockHandler::class, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Lock\Store\SemaphoreStore::class, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Lock\Store\FlockStore::class), \E_USER_DEPRECATED); +/** + * LockHandler class provides a simple abstraction to lock anything by means of + * a file lock. + * + * A locked file is created based on the lock name when calling lock(). Other + * lock handlers will not be able to lock the same name until it is released + * (explicitly by calling release() or implicitly when the instance holding the + * lock is destroyed). + * + * @author Grégoire Pineau + * @author Romain Neutron + * @author Nicolas Grekas + * + * @deprecated since version 3.4, to be removed in 4.0. Use Symfony\Component\Lock\Store\SemaphoreStore or Symfony\Component\Lock\Store\FlockStore instead. + */ +class LockHandler +{ + private $file; + private $handle; + /** + * @param string $name The lock name + * @param string|null $lockPath The directory to store the lock. Default values will use temporary directory + * + * @throws IOException If the lock directory could not be created or is not writable + */ + public function __construct($name, $lockPath = null) + { + $lockPath = $lockPath ?: \sys_get_temp_dir(); + if (!\is_dir($lockPath)) { + $fs = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Filesystem(); + $fs->mkdir($lockPath); + } + if (!\is_writable($lockPath)) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException(\sprintf('The directory "%s" is not writable.', $lockPath), 0, null, $lockPath); + } + $this->file = \sprintf('%s/sf.%s.%s.lock', $lockPath, \preg_replace('/[^a-z0-9\\._-]+/i', '-', $name), \hash('sha256', $name)); + } + /** + * Lock the resource. + * + * @param bool $blocking Wait until the lock is released + * + * @return bool Returns true if the lock was acquired, false otherwise + * + * @throws IOException If the lock file could not be created or opened + */ + public function lock($blocking = \false) + { + if ($this->handle) { + return \true; + } + $error = null; + // Silence error reporting + \set_error_handler(function ($errno, $msg) use(&$error) { + $error = $msg; + }); + if (!($this->handle = \fopen($this->file, 'r+') ?: \fopen($this->file, 'r'))) { + if ($this->handle = \fopen($this->file, 'x')) { + \chmod($this->file, 0666); + } elseif (!($this->handle = \fopen($this->file, 'r+') ?: \fopen($this->file, 'r'))) { + \usleep(100); + // Give some time for chmod() to complete + $this->handle = \fopen($this->file, 'r+') ?: \fopen($this->file, 'r'); + } + } + \restore_error_handler(); + if (!$this->handle) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Filesystem\Exception\IOException($error, 0, null, $this->file); + } + // On Windows, even if PHP doc says the contrary, LOCK_NB works, see + // https://bugs.php.net/54129 + if (!\flock($this->handle, \LOCK_EX | ($blocking ? 0 : \LOCK_NB))) { + \fclose($this->handle); + $this->handle = null; + return \false; + } + return \true; + } + /** + * Release the resource. + */ + public function release() + { + if ($this->handle) { + \flock($this->handle, \LOCK_UN | \LOCK_NB); + \fclose($this->handle); + $this->handle = null; + } + } +} diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Catalogue/MergeOperation.php b/classes/Utilities/Misc/Symfony/Component/Translation/Catalogue/MergeOperation.php index e769816e..9f0440d5 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Catalogue/MergeOperation.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Catalogue/MergeOperation.php @@ -10,7 +10,6 @@ */ namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Catalogue; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogueInterface; /** * Merge operation between two catalogues as follows: * all = source ∪ target = {x: x ∈ source ∨ x ∈ target} @@ -28,10 +27,9 @@ class MergeOperation extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\T protected function processDomain($domain) { $this->messages[$domain] = ['all' => [], 'new' => [], 'obsolete' => []]; - $intlDomain = $domain . \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogueInterface::INTL_DOMAIN_SUFFIX; foreach ($this->source->all($domain) as $id => $message) { $this->messages[$domain]['all'][$id] = $message; - $this->result->add([$id => $message], $this->source->defines($id, $intlDomain) ? $intlDomain : $domain); + $this->result->add([$id => $message], $domain); if (null !== ($keyMetadata = $this->source->getMetadata($id, $domain))) { $this->result->setMetadata($id, $keyMetadata, $domain); } @@ -40,7 +38,7 @@ protected function processDomain($domain) if (!$this->source->has($id, $domain)) { $this->messages[$domain]['all'][$id] = $message; $this->messages[$domain]['new'][$id] = $message; - $this->result->add([$id => $message], $this->target->defines($id, $intlDomain) ? $intlDomain : $domain); + $this->result->add([$id => $message], $domain); if (null !== ($keyMetadata = $this->target->getMetadata($id, $domain))) { $this->result->setMetadata($id, $keyMetadata, $domain); } diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Catalogue/TargetOperation.php b/classes/Utilities/Misc/Symfony/Component/Translation/Catalogue/TargetOperation.php index 7a2b9806..6474c92c 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Catalogue/TargetOperation.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Catalogue/TargetOperation.php @@ -10,7 +10,6 @@ */ namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Catalogue; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogueInterface; /** * Target operation between two catalogues: * intersection = source ∩ target = {x: x ∈ source ∧ x ∈ target} @@ -29,7 +28,6 @@ class TargetOperation extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\ protected function processDomain($domain) { $this->messages[$domain] = ['all' => [], 'new' => [], 'obsolete' => []]; - $intlDomain = $domain . \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogueInterface::INTL_DOMAIN_SUFFIX; // For 'all' messages, the code can't be simplified as ``$this->messages[$domain]['all'] = $target->all($domain);``, // because doing so will drop messages like {x: x ∈ source ∧ x ∉ target.all ∧ x ∈ target.fallback} // @@ -41,7 +39,7 @@ protected function processDomain($domain) foreach ($this->source->all($domain) as $id => $message) { if ($this->target->has($id, $domain)) { $this->messages[$domain]['all'][$id] = $message; - $this->result->add([$id => $message], $this->target->defines($id, $intlDomain) ? $intlDomain : $domain); + $this->result->add([$id => $message], $domain); if (null !== ($keyMetadata = $this->source->getMetadata($id, $domain))) { $this->result->setMetadata($id, $keyMetadata, $domain); } @@ -53,7 +51,7 @@ protected function processDomain($domain) if (!$this->source->has($id, $domain)) { $this->messages[$domain]['all'][$id] = $message; $this->messages[$domain]['new'][$id] = $message; - $this->result->add([$id => $message], $this->target->defines($id, $intlDomain) ? $intlDomain : $domain); + $this->result->add([$id => $message], $domain); if (null !== ($keyMetadata = $this->target->getMetadata($id, $domain))) { $this->result->setMetadata($id, $keyMetadata, $domain); } diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Command/XliffLintCommand.php b/classes/Utilities/Misc/Symfony/Component/Translation/Command/XliffLintCommand.php index 0b95e10e..a96ab6d8 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Command/XliffLintCommand.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Command/XliffLintCommand.php @@ -12,12 +12,10 @@ use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Console\Command\Command; use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Console\Exception\RuntimeException; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Console\Input\InputArgument; use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Console\Input\InputInterface; use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Console\Input\InputOption; use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Console\Output\OutputInterface; use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Console\Style\SymfonyStyle; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Util\XliffUtils; /** * Validates XLIFF files syntax and outputs encountered errors. * @@ -32,26 +30,24 @@ class XliffLintCommand extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component private $displayCorrectFiles; private $directoryIteratorProvider; private $isReadableProvider; - private $requireStrictFileNames; - public function __construct(string $name = null, callable $directoryIteratorProvider = null, callable $isReadableProvider = null, bool $requireStrictFileNames = \true) + public function __construct($name = null, $directoryIteratorProvider = null, $isReadableProvider = null) { parent::__construct($name); $this->directoryIteratorProvider = $directoryIteratorProvider; $this->isReadableProvider = $isReadableProvider; - $this->requireStrictFileNames = $requireStrictFileNames; } /** * {@inheritdoc} */ protected function configure() { - $this->setDescription('Lints a XLIFF file and outputs encountered errors')->addArgument('filename', \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Console\Input\InputArgument::IS_ARRAY, 'A file, a directory or "-" for reading from STDIN')->addOption('format', null, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Console\Input\InputOption::VALUE_REQUIRED, 'The output format', 'txt')->setHelp(<<setDescription('Lints a XLIFF file and outputs encountered errors')->addArgument('filename', null, 'A file or a directory or STDIN')->addOption('format', null, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Console\Input\InputOption::VALUE_REQUIRED, 'The output format', 'txt')->setHelp(<<%command.name% command lints a XLIFF file and outputs to STDOUT the first encountered syntax error. You can validates XLIFF contents passed from STDIN: - cat filename | php %command.full_name% - + cat filename | php %command.full_name% You can also validate the syntax of a file: @@ -68,58 +64,42 @@ protected function configure() protected function execute(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Console\Input\InputInterface $input, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Console\Output\OutputInterface $output) { $io = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Console\Style\SymfonyStyle($input, $output); - $filenames = (array) $input->getArgument('filename'); + $filename = $input->getArgument('filename'); $this->format = $input->getOption('format'); $this->displayCorrectFiles = $output->isVerbose(); - if (['-'] === $filenames) { - return $this->display($io, [$this->validate(\file_get_contents('php://stdin'))]); - } - // @deprecated to be removed in 5.0 - if (!$filenames) { - if (0 !== \ftell(\STDIN)) { + if (!$filename) { + if (!($stdin = $this->getStdin())) { throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Console\Exception\RuntimeException('Please provide a filename or pipe file content to STDIN.'); } - @\trigger_error('Piping content from STDIN to the "lint:xliff" command without passing the dash symbol "-" as argument is deprecated since Symfony 4.4.', \E_USER_DEPRECATED); - return $this->display($io, [$this->validate(\file_get_contents('php://stdin'))]); + return $this->display($io, [$this->validate($stdin)]); + } + if (!$this->isReadable($filename)) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Console\Exception\RuntimeException(\sprintf('File or directory "%s" is not readable.', $filename)); } $filesInfo = []; - foreach ($filenames as $filename) { - if (!$this->isReadable($filename)) { - throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Console\Exception\RuntimeException(\sprintf('File or directory "%s" is not readable.', $filename)); - } - foreach ($this->getFiles($filename) as $file) { - $filesInfo[] = $this->validate(\file_get_contents($file), $file); - } + foreach ($this->getFiles($filename) as $file) { + $filesInfo[] = $this->validate(\file_get_contents($file), $file); } return $this->display($io, $filesInfo); } - private function validate(string $content, string $file = null) : array + private function validate($content, $file = null) { - $errors = []; // Avoid: Warning DOMDocument::loadXML(): Empty string supplied as input if ('' === \trim($content)) { return ['file' => $file, 'valid' => \true]; } - $internal = \libxml_use_internal_errors(\true); + \libxml_use_internal_errors(\true); $document = new \DOMDocument(); $document->loadXML($content); - if (null !== ($targetLanguage = $this->getTargetLanguageFromFile($document))) { - $normalizedLocale = \preg_quote(\str_replace('-', '_', $targetLanguage), '/'); - // strict file names require translation files to be named '____.locale.xlf' - // otherwise, both '____.locale.xlf' and 'locale.____.xlf' are allowed - // also, the regexp matching must be case-insensitive, as defined for 'target-language' values - // http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#target-language - $expectedFilenamePattern = $this->requireStrictFileNames ? \sprintf('/^.*\\.(?i:%s)\\.(?:xlf|xliff)/', $normalizedLocale) : \sprintf('/^(?:.*\\.(?i:%s)|(?i:%s)\\..*)\\.(?:xlf|xliff)/', $normalizedLocale, $normalizedLocale); - if (0 === \preg_match($expectedFilenamePattern, \basename($file))) { - $errors[] = ['line' => -1, 'column' => -1, 'message' => \sprintf('There is a mismatch between the language included in the file name ("%s") and the "%s" value used in the "target-language" attribute of the file.', \basename($file), $targetLanguage)]; - } - } - foreach (\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Util\XliffUtils::validateSchema($document) as $xmlError) { - $errors[] = ['line' => $xmlError['line'], 'column' => $xmlError['column'], 'message' => $xmlError['message']]; + if ($document->schemaValidate(__DIR__ . '/../Resources/schemas/xliff-core-1.2-strict.xsd')) { + return ['file' => $file, 'valid' => \true]; } + $errorMessages = \array_map(function ($error) { + return ['line' => $error->line, 'column' => $error->column, 'message' => \trim($error->message)]; + }, \libxml_get_errors()); \libxml_clear_errors(); - \libxml_use_internal_errors($internal); - return ['file' => $file, 'valid' => 0 === \count($errors), 'messages' => $errors]; + \libxml_use_internal_errors(\false); + return ['file' => $file, 'valid' => \false, 'messages' => $errorMessages]; } private function display(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Console\Style\SymfonyStyle $io, array $files) { @@ -167,7 +147,7 @@ private function displayJson(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\C $io->writeln(\json_encode($filesInfo, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES)); return \min($errors, 1); } - private function getFiles(string $fileOrDirectory) + private function getFiles($fileOrDirectory) { if (\is_file($fileOrDirectory)) { (yield new \SplFileInfo($fileOrDirectory)); @@ -180,33 +160,38 @@ private function getFiles(string $fileOrDirectory) (yield $file); } } - private function getDirectoryIterator(string $directory) + /** + * @return string|null + */ + private function getStdin() + { + if (0 !== \ftell(\STDIN)) { + return null; + } + $inputs = ''; + while (!\feof(\STDIN)) { + $inputs .= \fread(\STDIN, 1024); + } + return $inputs; + } + private function getDirectoryIterator($directory) { $default = function ($directory) { return new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS), \RecursiveIteratorIterator::LEAVES_ONLY); }; if (null !== $this->directoryIteratorProvider) { - return ($this->directoryIteratorProvider)($directory, $default); + return \call_user_func($this->directoryIteratorProvider, $directory, $default); } return $default($directory); } - private function isReadable(string $fileOrDirectory) + private function isReadable($fileOrDirectory) { $default = function ($fileOrDirectory) { return \is_readable($fileOrDirectory); }; if (null !== $this->isReadableProvider) { - return ($this->isReadableProvider)($fileOrDirectory, $default); + return \call_user_func($this->isReadableProvider, $fileOrDirectory, $default); } return $default($fileOrDirectory); } - private function getTargetLanguageFromFile(\DOMDocument $xliffContents) : ?string - { - foreach ($xliffContents->getElementsByTagName('file')[0]->attributes ?? [] as $attribute) { - if ('target-language' === $attribute->nodeName) { - return $attribute->nodeValue; - } - } - return null; - } } diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/DataCollector/TranslationDataCollector.php b/classes/Utilities/Misc/Symfony/Component/Translation/DataCollector/TranslationDataCollector.php index 5eef07fb..752f36c5 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/DataCollector/TranslationDataCollector.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/DataCollector/TranslationDataCollector.php @@ -17,8 +17,6 @@ use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\DataCollectorTranslator; /** * @author Abdellatif Ait boudad - * - * @final since Symfony 4.4 */ class TranslationDataCollector extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\HttpKernel\DataCollector\DataCollector implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface { @@ -33,19 +31,17 @@ public function __construct(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Tr public function lateCollect() { $messages = $this->sanitizeCollectedMessages($this->translator->getCollectedMessages()); - $this->data += $this->computeCount($messages); + $this->data = $this->computeCount($messages); $this->data['messages'] = $messages; + $this->data['locale'] = $this->translator->getLocale(); + $this->data['fallback_locales'] = $this->translator->getFallbackLocales(); $this->data = $this->cloneVar($this->data); } /** * {@inheritdoc} - * - * @param \Throwable|null $exception */ - public function collect(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\HttpFoundation\Request $request, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\HttpFoundation\Response $response) + public function collect(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\HttpFoundation\Request $request, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\HttpFoundation\Response $response, \Exception $exception = null) { - $this->data['locale'] = $this->translator->getLocale(); - $this->data['fallback_locales'] = $this->translator->getFallbackLocales(); } /** * {@inheritdoc} @@ -86,9 +82,6 @@ public function getLocale() { return !empty($this->data['locale']) ? $this->data['locale'] : null; } - /** - * @internal since Symfony 4.2 - */ public function getFallbackLocales() { return isset($this->data['fallback_locales']) && \count($this->data['fallback_locales']) > 0 ? $this->data['fallback_locales'] : []; @@ -100,7 +93,7 @@ public function getName() { return 'translation'; } - private function sanitizeCollectedMessages(array $messages) + private function sanitizeCollectedMessages($messages) { $result = []; foreach ($messages as $key => $message) { @@ -120,7 +113,7 @@ private function sanitizeCollectedMessages(array $messages) } return $result; } - private function computeCount(array $messages) + private function computeCount($messages) { $count = [\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\DataCollectorTranslator::MESSAGE_DEFINED => 0, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\DataCollectorTranslator::MESSAGE_MISSING => 0, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK => 0]; foreach ($messages as $message) { @@ -128,7 +121,7 @@ private function computeCount(array $messages) } return $count; } - private function sanitizeString(string $string, int $length = 80) + private function sanitizeString($string, $length = 80) { $string = \trim(\preg_replace('/\\s+/', ' ', $string)); if (\false !== ($encoding = \mb_detect_encoding($string, null, \true))) { diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/DataCollectorTranslator.php b/classes/Utilities/Misc/Symfony/Component/Translation/DataCollectorTranslator.php index a3792543..b103dfcf 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/DataCollectorTranslator.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/DataCollectorTranslator.php @@ -12,13 +12,10 @@ use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface; use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidArgumentException; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\LocaleAwareInterface; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\TranslatorInterface; /** * @author Abdellatif Ait boudad */ -class DataCollectorTranslator implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorInterface, \ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\TranslatorInterface, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorBagInterface, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface +class DataCollectorTranslator implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorInterface, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorBagInterface, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface { const MESSAGE_DEFINED = 0; const MESSAGE_MISSING = 1; @@ -31,13 +28,10 @@ class DataCollectorTranslator implements \ILAB\MediaCloud\Utilities\Misc\Symfony /** * @param TranslatorInterface $translator The translator must implement TranslatorBagInterface */ - public function __construct($translator) + public function __construct(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorInterface $translator) { - if (!$translator instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorInterface && !$translator instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\TranslatorInterface) { - throw new \TypeError(\sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, \ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); - } - if (!$translator instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorBagInterface || !$translator instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\LocaleAwareInterface) { - throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidArgumentException(\sprintf('The Translator "%s" must implement TranslatorInterface, TranslatorBagInterface and LocaleAwareInterface.', \get_class($translator))); + if (!$translator instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorBagInterface) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidArgumentException(\sprintf('The Translator "%s" must implement TranslatorInterface and TranslatorBagInterface.', \get_class($translator))); } $this->translator = $translator; } @@ -52,17 +46,11 @@ public function trans($id, array $parameters = [], $domain = null, $locale = nul } /** * {@inheritdoc} - * - * @deprecated since Symfony 4.2, use the trans() method instead with a %count% parameter */ public function transChoice($id, $number, array $parameters = [], $domain = null, $locale = null) { - if ($this->translator instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\TranslatorInterface) { - $trans = $this->translator->trans($id, ['%count%' => $number] + $parameters, $domain, $locale); - } else { - $trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale); - } - $this->collectMessage($locale, $domain, $id, $trans, ['%count%' => $number] + $parameters); + $trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale); + $this->collectMessage($locale, $domain, $id, $trans, $parameters, $number); return $trans; } /** @@ -112,7 +100,7 @@ public function getFallbackLocales() */ public function __call($method, $args) { - return $this->translator->{$method}(...$args); + return \call_user_func_array([$this->translator, $method], $args); } /** * @return array @@ -121,7 +109,15 @@ public function getCollectedMessages() { return $this->messages; } - private function collectMessage(?string $locale, ?string $domain, ?string $id, string $translation, ?array $parameters = []) + /** + * @param string|null $locale + * @param string|null $domain + * @param string $id + * @param string $translation + * @param array|null $parameters + * @param int|null $number + */ + private function collectMessage($locale, $domain, $id, $translation, $parameters = [], $number = null) { if (null === $domain) { $domain = 'messages'; @@ -145,6 +141,6 @@ private function collectMessage(?string $locale, ?string $domain, ?string $id, s } else { $state = self::MESSAGE_MISSING; } - $this->messages[] = ['locale' => $locale, 'fallbackLocale' => $fallbackLocale, 'domain' => $domain, 'id' => $id, 'translation' => $translation, 'parameters' => $parameters, 'state' => $state, 'transChoiceNumber' => isset($parameters['%count%']) && \is_numeric($parameters['%count%']) ? $parameters['%count%'] : null]; + $this->messages[] = ['locale' => $locale, 'fallbackLocale' => $fallbackLocale, 'domain' => $domain, 'id' => $id, 'translation' => $translation, 'parameters' => $parameters, 'transChoiceNumber' => $number, 'state' => $state]; } } diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/DependencyInjection/TranslationDumperPass.php b/classes/Utilities/Misc/Symfony/Component/Translation/DependencyInjection/TranslationDumperPass.php index 32533e27..46b07308 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/DependencyInjection/TranslationDumperPass.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/DependencyInjection/TranslationDumperPass.php @@ -20,7 +20,7 @@ class TranslationDumperPass implements \ILAB\MediaCloud\Utilities\Misc\Symfony\C { private $writerServiceId; private $dumperTag; - public function __construct(string $writerServiceId = 'translation.writer', string $dumperTag = 'translation.dumper') + public function __construct($writerServiceId = 'translation.writer', $dumperTag = 'translation.dumper') { $this->writerServiceId = $writerServiceId; $this->dumperTag = $dumperTag; diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/DependencyInjection/TranslationExtractorPass.php b/classes/Utilities/Misc/Symfony/Component/Translation/DependencyInjection/TranslationExtractorPass.php index 2504c5a5..35a5bad8 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/DependencyInjection/TranslationExtractorPass.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/DependencyInjection/TranslationExtractorPass.php @@ -21,7 +21,7 @@ class TranslationExtractorPass implements \ILAB\MediaCloud\Utilities\Misc\Symfon { private $extractorServiceId; private $extractorTag; - public function __construct(string $extractorServiceId = 'translation.extractor', string $extractorTag = 'translation.extractor') + public function __construct($extractorServiceId = 'translation.extractor', $extractorTag = 'translation.extractor') { $this->extractorServiceId = $extractorServiceId; $this->extractorTag = $extractorTag; diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/DependencyInjection/TranslatorPass.php b/classes/Utilities/Misc/Symfony/Component/Translation/DependencyInjection/TranslatorPass.php index ad641516..0cbe9480 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/DependencyInjection/TranslatorPass.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/DependencyInjection/TranslatorPass.php @@ -21,8 +21,11 @@ class TranslatorPass implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Componen private $loaderTag; private $debugCommandServiceId; private $updateCommandServiceId; - public function __construct(string $translatorServiceId = 'translator.default', string $readerServiceId = 'translation.reader', string $loaderTag = 'translation.loader', string $debugCommandServiceId = 'console.command.translation_debug', string $updateCommandServiceId = 'console.command.translation_update') + public function __construct($translatorServiceId = 'translator.default', $readerServiceId = 'translation.loader', $loaderTag = 'translation.loader', $debugCommandServiceId = 'console.command.translation_debug', $updateCommandServiceId = 'console.command.translation_update') { + if ('translation.loader' === $readerServiceId && 2 > \func_num_args()) { + @\trigger_error(\sprintf('The default value for $readerServiceId in "%s()" will change in 4.0 to "translation.reader".', __METHOD__), \E_USER_DEPRECATED); + } $this->translatorServiceId = $translatorServiceId; $this->readerServiceId = $readerServiceId; $this->loaderTag = $loaderTag; @@ -51,24 +54,26 @@ public function process(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Depend } } } + // Duplicated code to support "translation.reader", to be removed in 4.0 + if ('translation.reader' !== $this->readerServiceId) { + if ($container->hasDefinition('translation.reader')) { + $definition = $container->getDefinition('translation.reader'); + foreach ($loaders as $id => $formats) { + foreach ($formats as $format) { + $definition->addMethodCall('addLoader', [$format, $loaderRefs[$id]]); + } + } + } + } $container->findDefinition($this->translatorServiceId)->replaceArgument(0, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass::register($container, $loaderRefs))->replaceArgument(3, $loaders); if (!$container->hasParameter('twig.default_path')) { return; } - $paths = \array_keys($container->getDefinition('twig.template_iterator')->getArgument(2)); if ($container->hasDefinition($this->debugCommandServiceId)) { - $definition = $container->getDefinition($this->debugCommandServiceId); - $definition->replaceArgument(4, $container->getParameter('twig.default_path')); - if (\count($definition->getArguments()) > 6) { - $definition->replaceArgument(6, $paths); - } + $container->getDefinition($this->debugCommandServiceId)->replaceArgument(4, $container->getParameter('twig.default_path')); } if ($container->hasDefinition($this->updateCommandServiceId)) { - $definition = $container->getDefinition($this->updateCommandServiceId); - $definition->replaceArgument(5, $container->getParameter('twig.default_path')); - if (\count($definition->getArguments()) > 7) { - $definition->replaceArgument(7, $paths); - } + $container->getDefinition($this->updateCommandServiceId)->replaceArgument(5, $container->getParameter('twig.default_path')); } } } diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/DependencyInjection/TranslatorPathsPass.php b/classes/Utilities/Misc/Symfony/Component/Translation/DependencyInjection/TranslatorPathsPass.php deleted file mode 100755 index 38ddbf4f..00000000 --- a/classes/Utilities/Misc/Symfony/Component/Translation/DependencyInjection/TranslatorPathsPass.php +++ /dev/null @@ -1,125 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\DependencyInjection; - -use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\Compiler\AbstractRecursivePass; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\ContainerBuilder; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\Definition; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\Reference; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\ServiceLocator; -/** - * @author Yonel Ceruto - */ -class TranslatorPathsPass extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\Compiler\AbstractRecursivePass -{ - private $translatorServiceId; - private $debugCommandServiceId; - private $updateCommandServiceId; - private $resolverServiceId; - private $level = 0; - private $paths = []; - private $definitions = []; - private $controllers = []; - public function __construct(string $translatorServiceId = 'translator', string $debugCommandServiceId = 'console.command.translation_debug', string $updateCommandServiceId = 'console.command.translation_update', string $resolverServiceId = 'argument_resolver.service') - { - $this->translatorServiceId = $translatorServiceId; - $this->debugCommandServiceId = $debugCommandServiceId; - $this->updateCommandServiceId = $updateCommandServiceId; - $this->resolverServiceId = $resolverServiceId; - } - public function process(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\ContainerBuilder $container) - { - if (!$container->hasDefinition($this->translatorServiceId)) { - return; - } - foreach ($this->findControllerArguments($container) as $controller => $argument) { - $id = \substr($controller, 0, \strpos($controller, ':') ?: \strlen($controller)); - if ($container->hasDefinition($id)) { - list($locatorRef) = $argument->getValues(); - $this->controllers[(string) $locatorRef][$container->getDefinition($id)->getClass()] = \true; - } - } - try { - parent::process($container); - $paths = []; - foreach ($this->paths as $class => $_) { - if (($r = $container->getReflectionClass($class)) && !$r->isInterface()) { - $paths[] = $r->getFileName(); - } - } - if ($paths) { - if ($container->hasDefinition($this->debugCommandServiceId)) { - $definition = $container->getDefinition($this->debugCommandServiceId); - $definition->replaceArgument(6, \array_merge($definition->getArgument(6), $paths)); - } - if ($container->hasDefinition($this->updateCommandServiceId)) { - $definition = $container->getDefinition($this->updateCommandServiceId); - $definition->replaceArgument(7, \array_merge($definition->getArgument(7), $paths)); - } - } - } finally { - $this->level = 0; - $this->paths = []; - $this->definitions = []; - } - } - protected function processValue($value, $isRoot = \false) - { - if ($value instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\Reference) { - if ((string) $value === $this->translatorServiceId) { - for ($i = $this->level - 1; $i >= 0; --$i) { - $class = $this->definitions[$i]->getClass(); - if (\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\ServiceLocator::class === $class) { - if (!isset($this->controllers[$this->currentId])) { - continue; - } - foreach ($this->controllers[$this->currentId] as $class => $_) { - $this->paths[$class] = \true; - } - } else { - $this->paths[$class] = \true; - } - break; - } - } - return $value; - } - if ($value instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\Definition) { - $this->definitions[$this->level++] = $value; - $value = parent::processValue($value, $isRoot); - unset($this->definitions[--$this->level]); - return $value; - } - return parent::processValue($value, $isRoot); - } - private function findControllerArguments(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\ContainerBuilder $container) : array - { - if ($container->hasDefinition($this->resolverServiceId)) { - $argument = $container->getDefinition($this->resolverServiceId)->getArgument(0); - if ($argument instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\Reference) { - $argument = $container->getDefinition($argument); - } - return $argument->getArgument(0); - } - if ($container->hasDefinition('debug.' . $this->resolverServiceId)) { - $argument = $container->getDefinition('debug.' . $this->resolverServiceId)->getArgument(0); - if ($argument instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\Reference) { - $argument = $container->getDefinition($argument); - } - $argument = $argument->getArgument(0); - if ($argument instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\DependencyInjection\Reference) { - $argument = $container->getDefinition($argument); - } - return $argument->getArgument(0); - } - return []; - } -} diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/DumperInterface.php b/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/DumperInterface.php index 441b6435..540132bd 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/DumperInterface.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/DumperInterface.php @@ -22,7 +22,8 @@ interface DumperInterface /** * Dumps the message catalogue. * - * @param array $options Options that are used by the dumper + * @param MessageCatalogue $messages The message catalogue + * @param array $options Options that are used by the dumper */ public function dump(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue $messages, $options = []); } diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/FileDumper.php b/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/FileDumper.php index 9c944e91..846fa02f 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/FileDumper.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/FileDumper.php @@ -15,6 +15,7 @@ use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue; /** * FileDumper is an implementation of DumperInterface that dump a message catalogue to file(s). + * Performs backup of already existing files. * * Options: * - path (mandatory): the directory where the files should be saved @@ -29,6 +30,12 @@ abstract class FileDumper implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Com * @var string */ protected $relativePathTemplate = '%domain%.%locale%.%extension%'; + /** + * Make file backup before the dump. + * + * @var bool + */ + private $backup = \true; /** * Sets the template for the relative paths to files. * @@ -42,15 +49,10 @@ public function setRelativePathTemplate($relativePathTemplate) * Sets backup flag. * * @param bool $backup - * - * @deprecated since Symfony 4.1 */ public function setBackup($backup) { - @\trigger_error(\sprintf('The "%s()" method is deprecated since Symfony 4.1.', __METHOD__), \E_USER_DEPRECATED); - if (\false !== $backup) { - throw new \LogicException('The backup feature is no longer supported.'); - } + $this->backup = $backup; } /** * {@inheritdoc} @@ -62,28 +64,20 @@ public function dump(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translati } // save a file for each domain foreach ($messages->getDomains() as $domain) { + // backup $fullpath = $options['path'] . '/' . $this->getRelativePath($domain, $messages->getLocale()); - if (!\file_exists($fullpath)) { + if (\file_exists($fullpath)) { + if ($this->backup) { + @\trigger_error('Creating a backup while dumping a message catalogue is deprecated since Symfony 3.1 and will be removed in 4.0. Use TranslationWriter::disableBackup() to disable the backup.', \E_USER_DEPRECATED); + \copy($fullpath, $fullpath . '~'); + } + } else { $directory = \dirname($fullpath); if (!\file_exists($directory) && !@\mkdir($directory, 0777, \true)) { throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\RuntimeException(\sprintf('Unable to create directory "%s".', $directory)); } } - $intlDomain = $domain . \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue::INTL_DOMAIN_SUFFIX; - $intlMessages = $messages->all($intlDomain); - if ($intlMessages) { - $intlPath = $options['path'] . '/' . $this->getRelativePath($intlDomain, $messages->getLocale()); - \file_put_contents($intlPath, $this->formatCatalogue($messages, $intlDomain, $options)); - $messages->replace([], $intlDomain); - try { - if ($messages->all($domain)) { - \file_put_contents($fullpath, $this->formatCatalogue($messages, $domain, $options)); - } - continue; - } finally { - $messages->replace($intlMessages, $intlDomain); - } - } + // save file \file_put_contents($fullpath, $this->formatCatalogue($messages, $domain, $options)); } } @@ -103,8 +97,13 @@ public abstract function formatCatalogue(\ILAB\MediaCloud\Utilities\Misc\Symfony protected abstract function getExtension(); /** * Gets the relative file path using the template. + * + * @param string $domain The domain + * @param string $locale The locale + * + * @return string The relative file path */ - private function getRelativePath(string $domain, string $locale) : string + private function getRelativePath($domain, $locale) { return \strtr($this->relativePathTemplate, ['%domain%' => $domain, '%locale%' => $locale, '%extension%' => $this->getExtension()]); } diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/IcuResFileDumper.php b/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/IcuResFileDumper.php index ce1e9d54..b24816f0 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/IcuResFileDumper.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/IcuResFileDumper.php @@ -86,12 +86,12 @@ public function formatCatalogue(\ILAB\MediaCloud\Utilities\Misc\Symfony\Componen ); return $header . $root . $data; } - private function writePadding(string $data) : ?string + private function writePadding($data) { $padding = \strlen($data) % 4; return $padding ? \str_repeat("ª", 4 - $padding) : null; } - private function getPosition(string $data) + private function getPosition($data) { return (\strlen($data) + 28) / 4; } diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/JsonFileDumper.php b/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/JsonFileDumper.php index f6b248bf..fbbdb128 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/JsonFileDumper.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/JsonFileDumper.php @@ -23,7 +23,11 @@ class JsonFileDumper extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\T */ public function formatCatalogue(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue $messages, $domain, array $options = []) { - $flags = $options['json_encoding'] ?? \JSON_PRETTY_PRINT; + if (isset($options['json_encoding'])) { + $flags = $options['json_encoding']; + } else { + $flags = \defined('JSON_PRETTY_PRINT') ? \JSON_PRETTY_PRINT : 0; + } return \json_encode($messages->all($domain), $flags); } /** diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/MoFileDumper.php b/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/MoFileDumper.php index 9ac6d82c..7b9cc984 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/MoFileDumper.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/MoFileDumper.php @@ -50,7 +50,7 @@ protected function getExtension() { return 'mo'; } - private function writeLong($str) : string + private function writeLong($str) { return \pack('V*', $str); } diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/PoFileDumper.php b/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/PoFileDumper.php index 203408c3..0d86fe2a 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/PoFileDumper.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/PoFileDumper.php @@ -36,69 +36,11 @@ public function formatCatalogue(\ILAB\MediaCloud\Utilities\Misc\Symfony\Componen } else { $newLine = \true; } - $metadata = $messages->getMetadata($source, $domain); - if (isset($metadata['comments'])) { - $output .= $this->formatComments($metadata['comments']); - } - if (isset($metadata['flags'])) { - $output .= $this->formatComments(\implode(',', (array) $metadata['flags']), ','); - } - if (isset($metadata['sources'])) { - $output .= $this->formatComments(\implode(' ', (array) $metadata['sources']), ':'); - } - $sourceRules = $this->getStandardRules($source); - $targetRules = $this->getStandardRules($target); - if (2 == \count($sourceRules) && $targetRules !== []) { - $output .= \sprintf('msgid "%s"' . "\n", $this->escape($sourceRules[0])); - $output .= \sprintf('msgid_plural "%s"' . "\n", $this->escape($sourceRules[1])); - foreach ($targetRules as $i => $targetRule) { - $output .= \sprintf('msgstr[%d] "%s"' . "\n", $i, $this->escape($targetRule)); - } - } else { - $output .= \sprintf('msgid "%s"' . "\n", $this->escape($source)); - $output .= \sprintf('msgstr "%s"' . "\n", $this->escape($target)); - } + $output .= \sprintf('msgid "%s"' . "\n", $this->escape($source)); + $output .= \sprintf('msgstr "%s"' . "\n", $this->escape($target)); } return $output; } - private function getStandardRules(string $id) - { - // Partly copied from TranslatorTrait::trans. - $parts = []; - if (\preg_match('/^\\|++$/', $id)) { - $parts = \explode('|', $id); - } elseif (\preg_match_all('/(?:\\|\\||[^\\|])++/', $id, $matches)) { - $parts = $matches[0]; - } - $intervalRegexp = <<<'EOF' -/^(?P - ({\s* - (\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*) - \s*}) - - | - - (?P[\[\]]) - \s* - (?P-Inf|\-?\d+(\.\d+)?) - \s*,\s* - (?P\+?Inf|\-?\d+(\.\d+)?) - \s* - (?P[\[\]]) -)\s*(?P.*?)$/xs -EOF; - $standardRules = []; - foreach ($parts as $part) { - $part = \trim(\str_replace('||', '|', $part)); - if (\preg_match($intervalRegexp, $part)) { - // Explicit rule is not a standard rule. - return []; - } else { - $standardRules[] = $part; - } - } - return $standardRules; - } /** * {@inheritdoc} */ @@ -106,16 +48,8 @@ protected function getExtension() { return 'po'; } - private function escape(string $str) : string + private function escape($str) { return \addcslashes($str, "\0..\37\"\\"); } - private function formatComments($comments, string $prefix = '') : ?string - { - $output = null; - foreach ((array) $comments as $comment) { - $output .= \sprintf('#%s %s' . "\n", $prefix, $comment); - } - return $output; - } } diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/QtFileDumper.php b/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/QtFileDumper.php index 9cdd207e..303d4702 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/QtFileDumper.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/QtFileDumper.php @@ -30,17 +30,6 @@ public function formatCatalogue(\ILAB\MediaCloud\Utilities\Misc\Symfony\Componen $context->appendChild($dom->createElement('name', $domain)); foreach ($messages->all($domain) as $source => $target) { $message = $context->appendChild($dom->createElement('message')); - $metadata = $messages->getMetadata($source, $domain); - if (isset($metadata['sources'])) { - foreach ((array) $metadata['sources'] as $location) { - $loc = \explode(':', $location, 2); - $location = $message->appendChild($dom->createElement('location')); - $location->setAttribute('filename', $loc[0]); - if (isset($loc[1])) { - $location->setAttribute('line', $loc[1]); - } - } - } $message->appendChild($dom->createElement('source', $source)); $message->appendChild($dom->createElement('translation', $target)); } diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/XliffFileDumper.php b/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/XliffFileDumper.php index 82c535bb..b5fa3547 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/XliffFileDumper.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/XliffFileDumper.php @@ -48,7 +48,7 @@ protected function getExtension() { return 'xlf'; } - private function dumpXliff1(string $defaultLocale, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue $messages, ?string $domain, array $options = []) + private function dumpXliff1($defaultLocale, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue $messages, $domain, array $options = []) { $toolInfo = ['tool-id' => 'symfony', 'tool-name' => 'Symfony']; if (\array_key_exists('tool_info', $options)) { @@ -106,7 +106,7 @@ private function dumpXliff1(string $defaultLocale, \ILAB\MediaCloud\Utilities\Mi } return $dom->saveXML(); } - private function dumpXliff2(string $defaultLocale, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue $messages, ?string $domain) + private function dumpXliff2($defaultLocale, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue $messages, $domain) { $dom = new \DOMDocument('1.0', 'utf-8'); $dom->formatOutput = \true; @@ -116,19 +116,10 @@ private function dumpXliff2(string $defaultLocale, \ILAB\MediaCloud\Utilities\Mi $xliff->setAttribute('srcLang', \str_replace('_', '-', $defaultLocale)); $xliff->setAttribute('trgLang', \str_replace('_', '-', $messages->getLocale())); $xliffFile = $xliff->appendChild($dom->createElement('file')); - if (\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue::INTL_DOMAIN_SUFFIX === \substr($domain, -($suffixLength = \strlen(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue::INTL_DOMAIN_SUFFIX)))) { - $xliffFile->setAttribute('id', \substr($domain, 0, -$suffixLength) . '.' . $messages->getLocale()); - } else { - $xliffFile->setAttribute('id', $domain . '.' . $messages->getLocale()); - } + $xliffFile->setAttribute('id', $domain . '.' . $messages->getLocale()); foreach ($messages->all($domain) as $source => $target) { $translation = $dom->createElement('unit'); $translation->setAttribute('id', \strtr(\substr(\base64_encode(\hash('sha256', $source, \true)), 0, 7), '/+', '._')); - $name = $source; - if (\strlen($source) > 80) { - $name = \substr(\md5($source), -7); - } - $translation->setAttribute('name', $name); $metadata = $messages->getMetadata($source, $domain); // Add notes section if ($this->hasMetadataArrayInfo('notes', $metadata)) { @@ -161,7 +152,13 @@ private function dumpXliff2(string $defaultLocale, \ILAB\MediaCloud\Utilities\Mi } return $dom->saveXML(); } - private function hasMetadataArrayInfo(string $key, array $metadata = null) : bool + /** + * @param string $key + * @param array|null $metadata + * + * @return bool + */ + private function hasMetadataArrayInfo($key, $metadata = null) { return null !== $metadata && \array_key_exists($key, $metadata) && ($metadata[$key] instanceof \Traversable || \is_array($metadata[$key])); } diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/YamlFileDumper.php b/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/YamlFileDumper.php index 424ae11d..056e9521 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/YamlFileDumper.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Dumper/YamlFileDumper.php @@ -22,7 +22,7 @@ class YamlFileDumper extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Dumper\FileDumper { private $extension; - public function __construct(string $extension = 'yml') + public function __construct($extension = 'yml') { $this->extension = $extension; } diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Exception/ExceptionInterface.php b/classes/Utilities/Misc/Symfony/Component/Translation/Exception/ExceptionInterface.php index 40b0d20b..e9d76153 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Exception/ExceptionInterface.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Exception/ExceptionInterface.php @@ -15,6 +15,6 @@ * * @author Fabien Potencier */ -interface ExceptionInterface extends \Throwable +interface ExceptionInterface { } diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Extractor/AbstractFileExtractor.php b/classes/Utilities/Misc/Symfony/Component/Translation/Extractor/AbstractFileExtractor.php index 8169e7cb..e8037554 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Extractor/AbstractFileExtractor.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Extractor/AbstractFileExtractor.php @@ -25,7 +25,7 @@ abstract class AbstractFileExtractor */ protected function extractFiles($resource) { - if (\is_iterable($resource)) { + if (\is_array($resource) || $resource instanceof \Traversable) { $files = []; foreach ($resource as $file) { if ($this->canBeExtracted($file)) { @@ -39,9 +39,14 @@ protected function extractFiles($resource) } return $files; } - private function toSplFileInfo(string $file) : \SplFileInfo + /** + * @param string $file + * + * @return \SplFileInfo + */ + private function toSplFileInfo($file) { - return new \SplFileInfo($file); + return $file instanceof \SplFileInfo ? $file : new \SplFileInfo($file); } /** * @param string $file diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Extractor/ChainExtractor.php b/classes/Utilities/Misc/Symfony/Component/Translation/Extractor/ChainExtractor.php index c1d32333..53cf3f1b 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Extractor/ChainExtractor.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Extractor/ChainExtractor.php @@ -27,7 +27,8 @@ class ChainExtractor implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Componen /** * Adds a loader to the translation extractor. * - * @param string $format The format of the loader + * @param string $format The format of the loader + * @param ExtractorInterface $extractor The loader */ public function addExtractor($format, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Extractor\ExtractorInterface $extractor) { diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Extractor/ExtractorInterface.php b/classes/Utilities/Misc/Symfony/Component/Translation/Extractor/ExtractorInterface.php index 0ad4272c..053993d9 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Extractor/ExtractorInterface.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Extractor/ExtractorInterface.php @@ -22,7 +22,8 @@ interface ExtractorInterface /** * Extracts translation messages from files, a file or a directory to the catalogue. * - * @param string|array $resource Files, a file or a directory + * @param string|array $resource Files, a file or a directory + * @param MessageCatalogue $catalogue The catalogue */ public function extract($resource, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue $catalogue); /** diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Extractor/PhpExtractor.php b/classes/Utilities/Misc/Symfony/Component/Translation/Extractor/PhpExtractor.php index a62361a2..1545fa3d 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Extractor/PhpExtractor.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Extractor/PhpExtractor.php @@ -41,8 +41,11 @@ public function extract($resource, \ILAB\MediaCloud\Utilities\Misc\Symfony\Compo { $files = $this->extractFiles($resource); foreach ($files as $file) { - $this->parseTokens(\token_get_all(\file_get_contents($file)), $catalog, $file); - \gc_mem_caches(); + $this->parseTokens(\token_get_all(\file_get_contents($file)), $catalog); + if (\PHP_VERSION_ID >= 70000) { + // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 + \gc_mem_caches(); + } } } /** @@ -140,15 +143,10 @@ private function getValue(\Iterator $tokenIterator) /** * Extracts trans message from PHP tokens. * - * @param array $tokens - * @param string $filename + * @param array $tokens */ protected function parseTokens($tokens, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue $catalog) { - if (\func_num_args() < 3 && __CLASS__ !== \get_class($this) && __CLASS__ !== (new \ReflectionMethod($this, __FUNCTION__))->getDeclaringClass()->getName() && !$this instanceof \ILAB\MediaCloud\Utilities\Misc\PHPUnit\Framework\MockObject\MockObject && !$this instanceof \ILAB\MediaCloud\Utilities\Misc\Prophecy\Prophecy\ProphecySubjectInterface) { - @\trigger_error(\sprintf('The "%s()" method will have a new "string $filename" argument in version 5.0, not defining it is deprecated since Symfony 4.3.', __METHOD__), \E_USER_DEPRECATED); - } - $filename = 2 < \func_num_args() ? \func_get_arg(2) : ''; $tokenIterator = new \ArrayIterator($tokens); for ($key = 0; $key < $tokenIterator->count(); ++$key) { foreach ($this->sequences as $sequence) { @@ -179,10 +177,6 @@ protected function parseTokens($tokens, \ILAB\MediaCloud\Utilities\Misc\Symfony\ } if ($message) { $catalog->set($message, $this->prefix . $message, $domain); - $metadata = $catalog->getMetadata($message, $domain) ?? []; - $normalizedFilename = \preg_replace('{[\\\\/]+}', '/', $filename); - $metadata['sources'][] = $normalizedFilename . ':' . $tokens[$key][2]; - $catalog->setMetadata($message, $metadata, $domain); break; } } diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Extractor/PhpStringTokenParser.php b/classes/Utilities/Misc/Symfony/Component/Translation/Extractor/PhpStringTokenParser.php index 5ea95d2b..7bac839e 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Extractor/PhpStringTokenParser.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Extractor/PhpStringTokenParser.php @@ -82,7 +82,7 @@ public static function parseEscapeSequences($str, $quote) } return \preg_replace_callback('~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3})~', [__CLASS__, 'parseCallback'], $str); } - private static function parseCallback(array $matches) : string + private static function parseCallback($matches) { $str = $matches[1]; if (isset(self::$replacements[$str])) { diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Formatter/ChoiceMessageFormatterInterface.php b/classes/Utilities/Misc/Symfony/Component/Translation/Formatter/ChoiceMessageFormatterInterface.php index 9d5f2669..c1d2d3a5 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Formatter/ChoiceMessageFormatterInterface.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Formatter/ChoiceMessageFormatterInterface.php @@ -12,8 +12,6 @@ /** * @author Abdellatif Ait boudad - * - * @deprecated since Symfony 4.2, use MessageFormatterInterface::format() with a %count% parameter instead */ interface ChoiceMessageFormatterInterface { diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Formatter/IntlFormatter.php b/classes/Utilities/Misc/Symfony/Component/Translation/Formatter/IntlFormatter.php deleted file mode 100755 index 47458210..00000000 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Formatter/IntlFormatter.php +++ /dev/null @@ -1,53 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Formatter; - -use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidArgumentException; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\LogicException; -/** - * @author Guilherme Blanco - * @author Abdellatif Ait boudad - */ -class IntlFormatter implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Formatter\IntlFormatterInterface -{ - private $hasMessageFormatter; - private $cache = []; - /** - * {@inheritdoc} - */ - public function formatIntl(string $message, string $locale, array $parameters = []) : string - { - // MessageFormatter constructor throws an exception if the message is empty - if ('' === $message) { - return ''; - } - if (!($formatter = $this->cache[$locale][$message] ?? null)) { - if (!($this->hasMessageFormatter ?? ($this->hasMessageFormatter = \class_exists(\MessageFormatter::class)))) { - throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\LogicException('Cannot parse message translation: please install the "intl" PHP extension or the "symfony/polyfill-intl-messageformatter" package.'); - } - try { - $this->cache[$locale][$message] = $formatter = new \MessageFormatter($locale, $message); - } catch (\IntlException $e) { - throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidArgumentException(\sprintf('Invalid message format (error #%d): %s.', \intl_get_error_code(), \intl_get_error_message()), 0, $e); - } - } - foreach ($parameters as $key => $value) { - if (\in_array($key[0] ?? null, ['%', '{'], \true)) { - unset($parameters[$key]); - $parameters[\trim($key, '%{ }')] = $value; - } - } - if (\false === ($message = $formatter->format($parameters))) { - throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidArgumentException(\sprintf('Unable to format message (error #%s): %s.', $formatter->getErrorCode(), $formatter->getErrorMessage())); - } - return $message; - } -} diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Formatter/IntlFormatterInterface.php b/classes/Utilities/Misc/Symfony/Component/Translation/Formatter/IntlFormatterInterface.php deleted file mode 100755 index 76a27da3..00000000 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Formatter/IntlFormatterInterface.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Formatter; - -/** - * Formats ICU message patterns. - * - * @author Nicolas Grekas - */ -interface IntlFormatterInterface -{ - /** - * Formats a localized message using rules defined by ICU MessageFormat. - * - * @see http://icu-project.org/apiref/icu4c/classMessageFormat.html#details - */ - public function formatIntl(string $message, string $locale, array $parameters = []) : string; -} diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Formatter/MessageFormatter.php b/classes/Utilities/Misc/Symfony/Component/Translation/Formatter/MessageFormatter.php index 3595236c..a7e1565c 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Formatter/MessageFormatter.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Formatter/MessageFormatter.php @@ -10,59 +10,33 @@ */ namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Formatter; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\IdentityTranslator; use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageSelector; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\TranslatorInterface; /** * @author Abdellatif Ait boudad */ -class MessageFormatter implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Formatter\MessageFormatterInterface, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Formatter\IntlFormatterInterface, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Formatter\ChoiceMessageFormatterInterface +class MessageFormatter implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Formatter\MessageFormatterInterface, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Formatter\ChoiceMessageFormatterInterface { - private $translator; - private $intlFormatter; + private $selector; /** - * @param TranslatorInterface|null $translator An identity translator to use as selector for pluralization + * @param MessageSelector|null $selector The message selector for pluralization */ - public function __construct($translator = null, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Formatter\IntlFormatterInterface $intlFormatter = null) + public function __construct(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageSelector $selector = null) { - if ($translator instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageSelector) { - $translator = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\IdentityTranslator($translator); - } elseif (null !== $translator && !$translator instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\TranslatorInterface && !$translator instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorInterface) { - throw new \TypeError(\sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, \ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); - } - $this->translator = $translator ?? new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\IdentityTranslator(); - $this->intlFormatter = $intlFormatter ?? new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Formatter\IntlFormatter(); + $this->selector = $selector ?: new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageSelector(); } /** * {@inheritdoc} */ public function format($message, $locale, array $parameters = []) { - if ($this->translator instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\TranslatorInterface) { - return $this->translator->trans($message, $parameters, null, $locale); - } return \strtr($message, $parameters); } /** * {@inheritdoc} */ - public function formatIntl(string $message, string $locale, array $parameters = []) : string - { - return $this->intlFormatter->formatIntl($message, $locale, $parameters); - } - /** - * {@inheritdoc} - * - * @deprecated since Symfony 4.2, use format() with a %count% parameter instead - */ public function choiceFormat($message, $number, $locale, array $parameters = []) { - @\trigger_error(\sprintf('The "%s()" method is deprecated since Symfony 4.2, use the format() one instead with a %%count%% parameter.', __METHOD__), \E_USER_DEPRECATED); - $parameters = ['%count%' => $number] + $parameters; - if ($this->translator instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\TranslatorInterface) { - return $this->format($message, $locale, $parameters); - } - return $this->format($this->translator->transChoice($message, $number, [], null, $locale), $locale, $parameters); + $parameters = \array_merge(['%count%' => $number], $parameters); + return $this->format($this->selector->choose($message, (int) $number, $locale), $locale, $parameters); } } diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/IdentityTranslator.php b/classes/Utilities/Misc/Symfony/Component/Translation/IdentityTranslator.php index f4c28e29..266d11d2 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/IdentityTranslator.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/IdentityTranslator.php @@ -10,57 +10,48 @@ */ namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\TranslatorInterface; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\TranslatorTrait; /** * IdentityTranslator does not translate anything. * * @author Fabien Potencier */ -class IdentityTranslator implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorInterface, \ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\TranslatorInterface +class IdentityTranslator implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorInterface { - use TranslatorTrait { - trans as private doTrans; - setLocale as private doSetLocale; - } private $selector; + private $locale; + /** + * @param MessageSelector|null $selector The message selector for pluralization + */ public function __construct(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageSelector $selector = null) { - $this->selector = $selector; - if (__CLASS__ !== \get_class($this)) { - @\trigger_error(\sprintf('Calling "%s()" is deprecated since Symfony 4.2.', __METHOD__), \E_USER_DEPRECATED); - } + $this->selector = $selector ?: new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageSelector(); } /** * {@inheritdoc} */ - public function trans($id, array $parameters = [], $domain = null, $locale = null) + public function setLocale($locale) { - return $this->doTrans($id, $parameters, $domain, $locale); + $this->locale = $locale; } /** * {@inheritdoc} */ - public function setLocale($locale) + public function getLocale() { - $this->doSetLocale($locale); + return $this->locale ?: \Locale::getDefault(); } /** * {@inheritdoc} - * - * @deprecated since Symfony 4.2, use the trans() method instead with a %count% parameter */ - public function transChoice($id, $number, array $parameters = [], $domain = null, $locale = null) + public function trans($id, array $parameters = [], $domain = null, $locale = null) { - @\trigger_error(\sprintf('The "%s()" method is deprecated since Symfony 4.2, use the trans() one instead with a "%%count%%" parameter.', __METHOD__), \E_USER_DEPRECATED); - if ($this->selector) { - return \strtr($this->selector->choose((string) $id, $number, $locale ?: $this->getLocale()), $parameters); - } - return $this->trans($id, ['%count%' => $number] + $parameters, $domain, $locale); + return \strtr((string) $id, $parameters); } - private function getPluralizationRule(int $number, string $locale) : int + /** + * {@inheritdoc} + */ + public function transChoice($id, $number, array $parameters = [], $domain = null, $locale = null) { - return \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\PluralizationRules::get($number, $locale, \false); + return \strtr($this->selector->choose((string) $id, (int) $number, $locale ?: $this->getLocale()), $parameters); } } diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Interval.php b/classes/Utilities/Misc/Symfony/Component/Translation/Interval.php index 1e712dee..35a3226a 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Interval.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Interval.php @@ -10,7 +10,6 @@ */ namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation; -@\trigger_error(\sprintf('The "%s" class is deprecated since Symfony 4.2, use IdentityTranslator instead.', \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Interval::class), \E_USER_DEPRECATED); use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidArgumentException; /** * Tests if a given number belongs to a given math interval. @@ -31,7 +30,6 @@ * @author Fabien Potencier * * @see http://en.wikipedia.org/wiki/Interval_%28mathematics%29#The_ISO_notation - * @deprecated since Symfony 4.2, use IdentityTranslator instead */ class Interval { @@ -87,7 +85,7 @@ public static function getIntervalRegexp() (?P[\\[\\]]) EOF; } - private static function convertNumber(string $number) : float + private static function convertNumber($number) { if ('-Inf' === $number) { return \log(0); diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Loader/ArrayLoader.php b/classes/Utilities/Misc/Symfony/Component/Translation/Loader/ArrayLoader.php index 645c3d83..1c0d94a3 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Loader/ArrayLoader.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Loader/ArrayLoader.php @@ -23,7 +23,7 @@ class ArrayLoader implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\T */ public function load($resource, $locale, $domain = 'messages') { - $resource = $this->flatten($resource); + $this->flatten($resource); $catalogue = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue($locale); $catalogue->add($resource, $domain); return $catalogue; @@ -35,19 +35,28 @@ public function load($resource, $locale, $domain = 'messages') * 'key' => ['key2' => ['key3' => 'value']] * Becomes: * 'key.key2.key3' => 'value' + * + * This function takes an array by reference and will modify it + * + * @param array &$messages The array that will be flattened + * @param array $subnode Current subnode being parsed, used internally for recursive calls + * @param string $path Current path being parsed, used internally for recursive calls */ - private function flatten(array $messages) : array + private function flatten(array &$messages, array $subnode = null, $path = null) { - $result = []; - foreach ($messages as $key => $value) { + if (null === $subnode) { + $subnode =& $messages; + } + foreach ($subnode as $key => $value) { if (\is_array($value)) { - foreach ($this->flatten($value) as $k => $v) { - $result[$key . '.' . $k] = $v; + $nodePath = $path ? $path . '.' . $key : $key; + $this->flatten($messages, $value, $nodePath); + if (null === $path) { + unset($messages[$key]); } - } else { - $result[$key] = $value; + } elseif (null !== $path) { + $messages[$path . '.' . $key] = $value; } } - return $result; } } diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Loader/IcuDatFileLoader.php b/classes/Utilities/Misc/Symfony/Component/Translation/Loader/IcuDatFileLoader.php index f83c9443..c2ddb9ca 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Loader/IcuDatFileLoader.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Loader/IcuDatFileLoader.php @@ -35,6 +35,7 @@ public function load($resource, $locale, $domain = 'messages') try { $rb = new \ResourceBundle($locale, $resource); } catch (\Exception $e) { + // HHVM compatibility: constructor throws on invalid resource $rb = null; } if (!$rb) { diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Loader/IcuResFileLoader.php b/classes/Utilities/Misc/Symfony/Component/Translation/Loader/IcuResFileLoader.php index 5410136c..28f22b56 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Loader/IcuResFileLoader.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Loader/IcuResFileLoader.php @@ -35,6 +35,7 @@ public function load($resource, $locale, $domain = 'messages') try { $rb = new \ResourceBundle($locale, $resource); } catch (\Exception $e) { + // HHVM compatibility: constructor throws on invalid resource $rb = null; } if (!$rb) { diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Loader/JsonFileLoader.php b/classes/Utilities/Misc/Symfony/Component/Translation/Loader/JsonFileLoader.php index 48724d82..c5d92033 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Loader/JsonFileLoader.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Loader/JsonFileLoader.php @@ -34,8 +34,12 @@ protected function loadResource($resource) } /** * Translates JSON_ERROR_* constant into meaningful message. + * + * @param int $errorCode Error code returned by json_last_error() call + * + * @return string Message string */ - private function getJSONErrorMessage(int $errorCode) : string + private function getJSONErrorMessage($errorCode) { switch ($errorCode) { case \JSON_ERROR_DEPTH: diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Loader/MoFileLoader.php b/classes/Utilities/Misc/Symfony/Component/Translation/Loader/MoFileLoader.php index 8b890991..39ce7ead 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Loader/MoFileLoader.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Loader/MoFileLoader.php @@ -104,8 +104,11 @@ protected function loadResource($resource) * Reads an unsigned long from stream respecting endianness. * * @param resource $stream + * @param bool $isBigEndian + * + * @return int */ - private function readLong($stream, bool $isBigEndian) : int + private function readLong($stream, $isBigEndian) { $result = \unpack($isBigEndian ? 'N1' : 'V1', \fread($stream, 4)); $result = \current($result); diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Loader/XliffFileLoader.php b/classes/Utilities/Misc/Symfony/Component/Translation/Loader/XliffFileLoader.php index 7646a26b..c7e98d4f 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Loader/XliffFileLoader.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Loader/XliffFileLoader.php @@ -12,10 +12,10 @@ use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Resource\FileResource; use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Util\XmlUtils; +use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidArgumentException; use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidResourceException; use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\NotFoundResourceException; use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Util\XliffUtils; /** * XliffFileLoader loads translations from XLIFF files. * @@ -41,17 +41,15 @@ public function load($resource, $locale, $domain = 'messages') } return $catalogue; } - private function extract($resource, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue $catalogue, string $domain) + private function extract($resource, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue $catalogue, $domain) { try { $dom = \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\Util\XmlUtils::loadFile($resource); } catch (\InvalidArgumentException $e) { throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidResourceException(\sprintf('Unable to load "%s": %s', $resource, $e->getMessage()), $e->getCode(), $e); } - $xliffVersion = \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Util\XliffUtils::getVersionNumber($dom); - if ($errors = \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Util\XliffUtils::validateSchema($dom)) { - throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidResourceException(\sprintf('Invalid resource provided: "%s"; Errors: %s', $resource, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Util\XliffUtils::getErrorsAsString($errors))); - } + $xliffVersion = $this->getVersionNumber($dom); + $this->validateSchema($xliffVersion, $dom, $this->getSchema($xliffVersion)); if ('1.2' === $xliffVersion) { $this->extractXliff1($dom, $catalogue, $domain); } @@ -61,44 +59,46 @@ private function extract($resource, \ILAB\MediaCloud\Utilities\Misc\Symfony\Comp } /** * Extract messages and metadata from DOMDocument into a MessageCatalogue. + * + * @param \DOMDocument $dom Source to extract messages and metadata + * @param MessageCatalogue $catalogue Catalogue where we'll collect messages and metadata + * @param string $domain The domain */ - private function extractXliff1(\DOMDocument $dom, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue $catalogue, string $domain) + private function extractXliff1(\DOMDocument $dom, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue $catalogue, $domain) { $xml = \simplexml_import_dom($dom); $encoding = \strtoupper($dom->encoding); - $namespace = 'urn:oasis:names:tc:xliff:document:1.2'; - $xml->registerXPathNamespace('xliff', $namespace); - foreach ($xml->xpath('//xliff:file') as $file) { - $fileAttributes = $file->attributes(); - $file->registerXPathNamespace('xliff', $namespace); - foreach ($file->xpath('.//xliff:trans-unit') as $translation) { - $attributes = $translation->attributes(); - if (!(isset($attributes['resname']) || isset($translation->source))) { - continue; - } - $source = isset($attributes['resname']) && $attributes['resname'] ? $attributes['resname'] : $translation->source; - // If the xlf file has another encoding specified, try to convert it because - // simple_xml will always return utf-8 encoded values - $target = $this->utf8ToCharset((string) ($translation->target ?? $translation->source), $encoding); - $catalogue->set((string) $source, $target, $domain); - $metadata = ['source' => (string) $translation->source, 'file' => ['original' => (string) $fileAttributes['original']]]; - if ($notes = $this->parseNotesMetadata($translation->note, $encoding)) { - $metadata['notes'] = $notes; - } - if (isset($translation->target) && $translation->target->attributes()) { - $metadata['target-attributes'] = []; - foreach ($translation->target->attributes() as $key => $value) { - $metadata['target-attributes'][$key] = (string) $value; - } - } - if (isset($attributes['id'])) { - $metadata['id'] = (string) $attributes['id']; + $xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:1.2'); + foreach ($xml->xpath('//xliff:trans-unit') as $translation) { + $attributes = $translation->attributes(); + if (!(isset($attributes['resname']) || isset($translation->source))) { + continue; + } + $source = isset($attributes['resname']) && $attributes['resname'] ? $attributes['resname'] : $translation->source; + // If the xlf file has another encoding specified, try to convert it because + // simple_xml will always return utf-8 encoded values + $target = $this->utf8ToCharset((string) (isset($translation->target) ? $translation->target : $translation->source), $encoding); + $catalogue->set((string) $source, $target, $domain); + $metadata = []; + if ($notes = $this->parseNotesMetadata($translation->note, $encoding)) { + $metadata['notes'] = $notes; + } + if (isset($translation->target) && $translation->target->attributes()) { + $metadata['target-attributes'] = []; + foreach ($translation->target->attributes() as $key => $value) { + $metadata['target-attributes'][$key] = (string) $value; } - $catalogue->setMetadata((string) $source, $metadata, $domain); } + if (isset($attributes['id'])) { + $metadata['id'] = (string) $attributes['id']; + } + $catalogue->setMetadata((string) $source, $metadata, $domain); } } - private function extractXliff2(\DOMDocument $dom, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue $catalogue, string $domain) + /** + * @param string $domain + */ + private function extractXliff2(\DOMDocument $dom, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue $catalogue, $domain) { $xml = \simplexml_import_dom($dom); $encoding = \strtoupper($dom->encoding); @@ -134,15 +134,130 @@ private function extractXliff2(\DOMDocument $dom, \ILAB\MediaCloud\Utilities\Mis } /** * Convert a UTF8 string to the specified encoding. + * + * @param string $content String to decode + * @param string $encoding Target encoding + * + * @return string */ - private function utf8ToCharset(string $content, string $encoding = null) : string + private function utf8ToCharset($content, $encoding = null) { if ('UTF-8' !== $encoding && !empty($encoding)) { return \mb_convert_encoding($content, $encoding, 'UTF-8'); } return $content; } - private function parseNotesMetadata(\SimpleXMLElement $noteElement = null, string $encoding = null) : array + /** + * Validates and parses the given file into a DOMDocument. + * + * @param string $file + * @param string $schema source of the schema + * + * @throws InvalidResourceException + */ + private function validateSchema($file, \DOMDocument $dom, $schema) + { + $internalErrors = \libxml_use_internal_errors(\true); + $disableEntities = \libxml_disable_entity_loader(\false); + if (!@$dom->schemaValidateSource($schema)) { + \libxml_disable_entity_loader($disableEntities); + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidResourceException(\sprintf('Invalid resource provided: "%s"; Errors: %s', $file, \implode("\n", $this->getXmlErrors($internalErrors)))); + } + \libxml_disable_entity_loader($disableEntities); + $dom->normalizeDocument(); + \libxml_clear_errors(); + \libxml_use_internal_errors($internalErrors); + } + private function getSchema($xliffVersion) + { + if ('1.2' === $xliffVersion) { + $schemaSource = \file_get_contents(__DIR__ . '/schema/dic/xliff-core/xliff-core-1.2-strict.xsd'); + $xmlUri = 'http://www.w3.org/2001/xml.xsd'; + } elseif ('2.0' === $xliffVersion) { + $schemaSource = \file_get_contents(__DIR__ . '/schema/dic/xliff-core/xliff-core-2.0.xsd'); + $xmlUri = 'informativeCopiesOf3rdPartySchemas/w3c/xml.xsd'; + } else { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidArgumentException(\sprintf('No support implemented for loading XLIFF version "%s".', $xliffVersion)); + } + return $this->fixXmlLocation($schemaSource, $xmlUri); + } + /** + * Internally changes the URI of a dependent xsd to be loaded locally. + * + * @param string $schemaSource Current content of schema file + * @param string $xmlUri External URI of XML to convert to local + * + * @return string + */ + private function fixXmlLocation($schemaSource, $xmlUri) + { + $newPath = \str_replace('\\', '/', __DIR__) . '/schema/dic/xliff-core/xml.xsd'; + $parts = \explode('/', $newPath); + $locationstart = 'file:///'; + if (0 === \stripos($newPath, 'phar://')) { + $tmpfile = \tempnam(\sys_get_temp_dir(), 'symfony'); + if ($tmpfile) { + \copy($newPath, $tmpfile); + $parts = \explode('/', \str_replace('\\', '/', $tmpfile)); + } else { + \array_shift($parts); + $locationstart = 'phar:///'; + } + } + $drive = '\\' === \DIRECTORY_SEPARATOR ? \array_shift($parts) . '/' : ''; + $newPath = $locationstart . $drive . \implode('/', \array_map('rawurlencode', $parts)); + return \str_replace($xmlUri, $newPath, $schemaSource); + } + /** + * Returns the XML errors of the internal XML parser. + * + * @param bool $internalErrors + * + * @return array An array of errors + */ + private function getXmlErrors($internalErrors) + { + $errors = []; + foreach (\libxml_get_errors() as $error) { + $errors[] = \sprintf('[%s %s] %s (in %s - line %d, column %d)', \LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR', $error->code, \trim($error->message), $error->file ?: 'n/a', $error->line, $error->column); + } + \libxml_clear_errors(); + \libxml_use_internal_errors($internalErrors); + return $errors; + } + /** + * Gets xliff file version based on the root "version" attribute. + * Defaults to 1.2 for backwards compatibility. + * + * @throws InvalidArgumentException + * + * @return string + */ + private function getVersionNumber(\DOMDocument $dom) + { + /** @var \DOMNode $xliff */ + foreach ($dom->getElementsByTagName('xliff') as $xliff) { + $version = $xliff->attributes->getNamedItem('version'); + if ($version) { + return $version->nodeValue; + } + $namespace = $xliff->attributes->getNamedItem('xmlns'); + if ($namespace) { + if (0 !== \substr_compare('urn:oasis:names:tc:xliff:document:', $namespace->nodeValue, 0, 34)) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidArgumentException(\sprintf('Not a valid XLIFF namespace "%s"', $namespace)); + } + return \substr($namespace, 34); + } + } + // Falls back to v1.2 + return '1.2'; + } + /** + * @param string|null $encoding + * + * @return array + */ + private function parseNotesMetadata(\SimpleXMLElement $noteElement = null, $encoding = null) { $notes = []; if (null === $noteElement) { diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Loader/YamlFileLoader.php b/classes/Utilities/Misc/Symfony/Component/Translation/Loader/YamlFileLoader.php index 9081edfb..ff0c42e8 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Loader/YamlFileLoader.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Loader/YamlFileLoader.php @@ -14,7 +14,6 @@ use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\LogicException; use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Yaml\Exception\ParseException; use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Yaml\Parser as YamlParser; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Yaml\Yaml; /** * YamlFileLoader loads translations from Yaml files. * @@ -34,10 +33,16 @@ protected function loadResource($resource) } $this->yamlParser = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Yaml\Parser(); } + $prevErrorHandler = \set_error_handler(function ($level, $message, $script, $line) use($resource, &$prevErrorHandler) { + $message = \E_USER_DEPRECATED === $level ? \preg_replace('/ on line \\d+/', ' in "' . $resource . '"$0', $message) : $message; + return $prevErrorHandler ? $prevErrorHandler($level, $message, $script, $line) : \false; + }); try { - $messages = $this->yamlParser->parseFile($resource, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Yaml\Yaml::PARSE_CONSTANT); + $messages = $this->yamlParser->parseFile($resource); } catch (\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Yaml\Exception\ParseException $e) { throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidResourceException(\sprintf('Error parsing YAML, invalid file "%s"', $resource), 0, $e); + } finally { + \restore_error_handler(); } if (null !== $messages && !\is_array($messages)) { throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidResourceException(\sprintf('Unable to load file "%s".', $resource)); diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Loader/schema/dic/xliff-core/xliff-core-1.2-strict.xsd b/classes/Utilities/Misc/Symfony/Component/Translation/Loader/schema/dic/xliff-core/xliff-core-1.2-strict.xsd new file mode 100755 index 00000000..3ce2a8e8 --- /dev/null +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Loader/schema/dic/xliff-core/xliff-core-1.2-strict.xsd @@ -0,0 +1,2223 @@ + + + + + + + + + + + + + + + Values for the attribute 'context-type'. + + + + + Indicates a database content. + + + + + Indicates the content of an element within an XML document. + + + + + Indicates the name of an element within an XML document. + + + + + Indicates the line number from the sourcefile (see context-type="sourcefile") where the <source> is found. + + + + + Indicates a the number of parameters contained within the <source>. + + + + + Indicates notes pertaining to the parameters in the <source>. + + + + + Indicates the content of a record within a database. + + + + + Indicates the name of a record within a database. + + + + + Indicates the original source file in the case that multiple files are merged to form the original file from which the XLIFF file is created. This differs from the original <file> attribute in that this sourcefile is one of many that make up that file. + + + + + + + Values for the attribute 'count-type'. + + + + + Indicates the count units are items that are used X times in a certain context; example: this is a reusable text unit which is used 42 times in other texts. + + + + + Indicates the count units are translation units existing already in the same document. + + + + + Indicates a total count. + + + + + + + Values for the attribute 'ctype' when used other elements than <ph> or <x>. + + + + + Indicates a run of bolded text. + + + + + Indicates a run of text in italics. + + + + + Indicates a run of underlined text. + + + + + Indicates a run of hyper-text. + + + + + + + Values for the attribute 'ctype' when used with <ph> or <x>. + + + + + Indicates a inline image. + + + + + Indicates a page break. + + + + + Indicates a line break. + + + + + + + + + + + + Values for the attribute 'datatype'. + + + + + Indicates Active Server Page data. + + + + + Indicates C source file data. + + + + + Indicates Channel Definition Format (CDF) data. + + + + + Indicates ColdFusion data. + + + + + Indicates C++ source file data. + + + + + Indicates C-Sharp data. + + + + + Indicates strings from C, ASM, and driver files data. + + + + + Indicates comma-separated values data. + + + + + Indicates database data. + + + + + Indicates portions of document that follows data and contains metadata. + + + + + Indicates portions of document that precedes data and contains metadata. + + + + + Indicates data from standard UI file operations dialogs (e.g., Open, Save, Save As, Export, Import). + + + + + Indicates standard user input screen data. + + + + + Indicates HyperText Markup Language (HTML) data - document instance. + + + + + Indicates content within an HTML document’s <body> element. + + + + + Indicates Windows INI file data. + + + + + Indicates Interleaf data. + + + + + Indicates Java source file data (extension '.java'). + + + + + Indicates Java property resource bundle data. + + + + + Indicates Java list resource bundle data. + + + + + Indicates JavaScript source file data. + + + + + Indicates JScript source file data. + + + + + Indicates information relating to formatting. + + + + + Indicates LISP source file data. + + + + + Indicates information relating to margin formats. + + + + + Indicates a file containing menu. + + + + + Indicates numerically identified string table. + + + + + Indicates Maker Interchange Format (MIF) data. + + + + + Indicates that the datatype attribute value is a MIME Type value and is defined in the mime-type attribute. + + + + + Indicates GNU Machine Object data. + + + + + Indicates Message Librarian strings created by Novell's Message Librarian Tool. + + + + + Indicates information to be displayed at the bottom of each page of a document. + + + + + Indicates information to be displayed at the top of each page of a document. + + + + + Indicates a list of property values (e.g., settings within INI files or preferences dialog). + + + + + Indicates Pascal source file data. + + + + + Indicates Hypertext Preprocessor data. + + + + + Indicates plain text file (no formatting other than, possibly, wrapping). + + + + + Indicates GNU Portable Object file. + + + + + Indicates dynamically generated user defined document. e.g. Oracle Report, Crystal Report, etc. + + + + + Indicates Windows .NET binary resources. + + + + + Indicates Windows .NET Resources. + + + + + Indicates Rich Text Format (RTF) data. + + + + + Indicates Standard Generalized Markup Language (SGML) data - document instance. + + + + + Indicates Standard Generalized Markup Language (SGML) data - Document Type Definition (DTD). + + + + + Indicates Scalable Vector Graphic (SVG) data. + + + + + Indicates VisualBasic Script source file. + + + + + Indicates warning message. + + + + + Indicates Windows (Win32) resources (i.e. resources extracted from an RC script, a message file, or a compiled file). + + + + + Indicates Extensible HyperText Markup Language (XHTML) data - document instance. + + + + + Indicates Extensible Markup Language (XML) data - document instance. + + + + + Indicates Extensible Markup Language (XML) data - Document Type Definition (DTD). + + + + + Indicates Extensible Stylesheet Language (XSL) data. + + + + + Indicates XUL elements. + + + + + + + Values for the attribute 'mtype'. + + + + + Indicates the marked text is an abbreviation. + + + + + ISO-12620 2.1.8: A term resulting from the omission of any part of the full term while designating the same concept. + + + + + ISO-12620 2.1.8.1: An abbreviated form of a simple term resulting from the omission of some of its letters (e.g. 'adj.' for 'adjective'). + + + + + ISO-12620 2.1.8.4: An abbreviated form of a term made up of letters from the full form of a multiword term strung together into a sequence pronounced only syllabically (e.g. 'radar' for 'radio detecting and ranging'). + + + + + ISO-12620: A proper-name term, such as the name of an agency or other proper entity. + + + + + ISO-12620 2.1.18.1: A recurrent word combination characterized by cohesion in that the components of the collocation must co-occur within an utterance or series of utterances, even though they do not necessarily have to maintain immediate proximity to one another. + + + + + ISO-12620 2.1.5: A synonym for an international scientific term that is used in general discourse in a given language. + + + + + Indicates the marked text is a date and/or time. + + + + + ISO-12620 2.1.15: An expression used to represent a concept based on a statement that two mathematical expressions are, for instance, equal as identified by the equal sign (=), or assigned to one another by a similar sign. + + + + + ISO-12620 2.1.7: The complete representation of a term for which there is an abbreviated form. + + + + + ISO-12620 2.1.14: Figures, symbols or the like used to express a concept briefly, such as a mathematical or chemical formula. + + + + + ISO-12620 2.1.1: The concept designation that has been chosen to head a terminological record. + + + + + ISO-12620 2.1.8.3: An abbreviated form of a term consisting of some of the initial letters of the words making up a multiword term or the term elements making up a compound term when these letters are pronounced individually (e.g. 'BSE' for 'bovine spongiform encephalopathy'). + + + + + ISO-12620 2.1.4: A term that is part of an international scientific nomenclature as adopted by an appropriate scientific body. + + + + + ISO-12620 2.1.6: A term that has the same or nearly identical orthographic or phonemic form in many languages. + + + + + ISO-12620 2.1.16: An expression used to represent a concept based on mathematical or logical relations, such as statements of inequality, set relationships, Boolean operations, and the like. + + + + + ISO-12620 2.1.17: A unit to track object. + + + + + Indicates the marked text is a name. + + + + + ISO-12620 2.1.3: A term that represents the same or a very similar concept as another term in the same language, but for which interchangeability is limited to some contexts and inapplicable in others. + + + + + ISO-12620 2.1.17.2: A unique alphanumeric designation assigned to an object in a manufacturing system. + + + + + Indicates the marked text is a phrase. + + + + + ISO-12620 2.1.18: Any group of two or more words that form a unit, the meaning of which frequently cannot be deduced based on the combined sense of the words making up the phrase. + + + + + Indicates the marked text should not be translated. + + + + + ISO-12620 2.1.12: A form of a term resulting from an operation whereby non-Latin writing systems are converted to the Latin alphabet. + + + + + Indicates that the marked text represents a segment. + + + + + ISO-12620 2.1.18.2: A fixed, lexicalized phrase. + + + + + ISO-12620 2.1.8.2: A variant of a multiword term that includes fewer words than the full form of the term (e.g. 'Group of Twenty-four' for 'Intergovernmental Group of Twenty-four on International Monetary Affairs'). + + + + + ISO-12620 2.1.17.1: Stock keeping unit, an inventory item identified by a unique alphanumeric designation assigned to an object in an inventory control system. + + + + + ISO-12620 2.1.19: A fixed chunk of recurring text. + + + + + ISO-12620 2.1.13: A designation of a concept by letters, numerals, pictograms or any combination thereof. + + + + + ISO-12620 2.1.2: Any term that represents the same or a very similar concept as the main entry term in a term entry. + + + + + ISO-12620 2.1.18.3: Phraseological unit in a language that expresses the same semantic content as another phrase in that same language. + + + + + Indicates the marked text is a term. + + + + + ISO-12620 2.1.11: A form of a term resulting from an operation whereby the characters of one writing system are represented by characters from another writing system, taking into account the pronunciation of the characters converted. + + + + + ISO-12620 2.1.10: A form of a term resulting from an operation whereby the characters of an alphabetic writing system are represented by characters from another alphabetic writing system. + + + + + ISO-12620 2.1.8.5: An abbreviated form of a term resulting from the omission of one or more term elements or syllables (e.g. 'flu' for 'influenza'). + + + + + ISO-12620 2.1.9: One of the alternate forms of a term. + + + + + + + Values for the attribute 'restype'. + + + + + Indicates a Windows RC AUTO3STATE control. + + + + + Indicates a Windows RC AUTOCHECKBOX control. + + + + + Indicates a Windows RC AUTORADIOBUTTON control. + + + + + Indicates a Windows RC BEDIT control. + + + + + Indicates a bitmap, for example a BITMAP resource in Windows. + + + + + Indicates a button object, for example a BUTTON control Windows. + + + + + Indicates a caption, such as the caption of a dialog box. + + + + + Indicates the cell in a table, for example the content of the <td> element in HTML. + + + + + Indicates check box object, for example a CHECKBOX control in Windows. + + + + + Indicates a menu item with an associated checkbox. + + + + + Indicates a list box, but with a check-box for each item. + + + + + Indicates a color selection dialog. + + + + + Indicates a combination of edit box and listbox object, for example a COMBOBOX control in Windows. + + + + + Indicates an initialization entry of an extended combobox DLGINIT resource block. (code 0x1234). + + + + + Indicates an initialization entry of a combobox DLGINIT resource block (code 0x0403). + + + + + Indicates a UI base class element that cannot be represented by any other element. + + + + + Indicates a context menu. + + + + + Indicates a Windows RC CTEXT control. + + + + + Indicates a cursor, for example a CURSOR resource in Windows. + + + + + Indicates a date/time picker. + + + + + Indicates a Windows RC DEFPUSHBUTTON control. + + + + + Indicates a dialog box. + + + + + Indicates a Windows RC DLGINIT resource block. + + + + + Indicates an edit box object, for example an EDIT control in Windows. + + + + + Indicates a filename. + + + + + Indicates a file dialog. + + + + + Indicates a footnote. + + + + + Indicates a font name. + + + + + Indicates a footer. + + + + + Indicates a frame object. + + + + + Indicates a XUL grid element. + + + + + Indicates a groupbox object, for example a GROUPBOX control in Windows. + + + + + Indicates a header item. + + + + + Indicates a heading, such has the content of <h1>, <h2>, etc. in HTML. + + + + + Indicates a Windows RC HEDIT control. + + + + + Indicates a horizontal scrollbar. + + + + + Indicates an icon, for example an ICON resource in Windows. + + + + + Indicates a Windows RC IEDIT control. + + + + + Indicates keyword list, such as the content of the Keywords meta-data in HTML, or a K footnote in WinHelp RTF. + + + + + Indicates a label object. + + + + + Indicates a label that is also a HTML link (not necessarily a URL). + + + + + Indicates a list (a group of list-items, for example an <ol> or <ul> element in HTML). + + + + + Indicates a listbox object, for example an LISTBOX control in Windows. + + + + + Indicates an list item (an entry in a list). + + + + + Indicates a Windows RC LTEXT control. + + + + + Indicates a menu (a group of menu-items). + + + + + Indicates a toolbar containing one or more tope level menus. + + + + + Indicates a menu item (an entry in a menu). + + + + + Indicates a XUL menuseparator element. + + + + + Indicates a message, for example an entry in a MESSAGETABLE resource in Windows. + + + + + Indicates a calendar control. + + + + + Indicates an edit box beside a spin control. + + + + + Indicates a catch all for rectangular areas. + + + + + Indicates a standalone menu not necessarily associated with a menubar. + + + + + Indicates a pushbox object, for example a PUSHBOX control in Windows. + + + + + Indicates a Windows RC PUSHBUTTON control. + + + + + Indicates a radio button object. + + + + + Indicates a menuitem with associated radio button. + + + + + Indicates raw data resources for an application. + + + + + Indicates a row in a table. + + + + + Indicates a Windows RC RTEXT control. + + + + + Indicates a user navigable container used to show a portion of a document. + + + + + Indicates a generic divider object (e.g. menu group separator). + + + + + Windows accelerators, shortcuts in resource or property files. + + + + + Indicates a UI control to indicate process activity but not progress. + + + + + Indicates a splitter bar. + + + + + Indicates a Windows RC STATE3 control. + + + + + Indicates a window for providing feedback to the users, like 'read-only', etc. + + + + + Indicates a string, for example an entry in a STRINGTABLE resource in Windows. + + + + + Indicates a layers of controls with a tab to select layers. + + + + + Indicates a display and edits regular two-dimensional tables of cells. + + + + + Indicates a XUL textbox element. + + + + + Indicates a UI button that can be toggled to on or off state. + + + + + Indicates an array of controls, usually buttons. + + + + + Indicates a pop up tool tip text. + + + + + Indicates a bar with a pointer indicating a position within a certain range. + + + + + Indicates a control that displays a set of hierarchical data. + + + + + Indicates a URI (URN or URL). + + + + + Indicates a Windows RC USERBUTTON control. + + + + + Indicates a user-defined control like CONTROL control in Windows. + + + + + Indicates the text of a variable. + + + + + Indicates version information about a resource like VERSIONINFO in Windows. + + + + + Indicates a vertical scrollbar. + + + + + Indicates a graphical window. + + + + + + + Values for the attribute 'size-unit'. + + + + + Indicates a size in 8-bit bytes. + + + + + Indicates a size in Unicode characters. + + + + + Indicates a size in columns. Used for HTML text area. + + + + + Indicates a size in centimeters. + + + + + Indicates a size in dialog units, as defined in Windows resources. + + + + + Indicates a size in 'font-size' units (as defined in CSS). + + + + + Indicates a size in 'x-height' units (as defined in CSS). + + + + + Indicates a size in glyphs. A glyph is considered to be one or more combined Unicode characters that represent a single displayable text character. Sometimes referred to as a 'grapheme cluster' + + + + + Indicates a size in inches. + + + + + Indicates a size in millimeters. + + + + + Indicates a size in percentage. + + + + + Indicates a size in pixels. + + + + + Indicates a size in point. + + + + + Indicates a size in rows. Used for HTML text area. + + + + + + + Values for the attribute 'state'. + + + + + Indicates the terminating state. + + + + + Indicates only non-textual information needs adaptation. + + + + + Indicates both text and non-textual information needs adaptation. + + + + + Indicates only non-textual information needs review. + + + + + Indicates both text and non-textual information needs review. + + + + + Indicates that only the text of the item needs to be reviewed. + + + + + Indicates that the item needs to be translated. + + + + + Indicates that the item is new. For example, translation units that were not in a previous version of the document. + + + + + Indicates that changes are reviewed and approved. + + + + + Indicates that the item has been translated. + + + + + + + Values for the attribute 'state-qualifier'. + + + + + Indicates an exact match. An exact match occurs when a source text of a segment is exactly the same as the source text of a segment that was translated previously. + + + + + Indicates a fuzzy match. A fuzzy match occurs when a source text of a segment is very similar to the source text of a segment that was translated previously (e.g. when the difference is casing, a few changed words, white-space discripancy, etc.). + + + + + Indicates a match based on matching IDs (in addition to matching text). + + + + + Indicates a translation derived from a glossary. + + + + + Indicates a translation derived from existing translation. + + + + + Indicates a translation derived from machine translation. + + + + + Indicates a translation derived from a translation repository. + + + + + Indicates a translation derived from a translation memory. + + + + + Indicates the translation is suggested by machine translation. + + + + + Indicates that the item has been rejected because of incorrect grammar. + + + + + Indicates that the item has been rejected because it is incorrect. + + + + + Indicates that the item has been rejected because it is too long or too short. + + + + + Indicates that the item has been rejected because of incorrect spelling. + + + + + Indicates the translation is suggested by translation memory. + + + + + + + Values for the attribute 'unit'. + + + + + Refers to words. + + + + + Refers to pages. + + + + + Refers to <trans-unit> elements. + + + + + Refers to <bin-unit> elements. + + + + + Refers to glyphs. + + + + + Refers to <trans-unit> and/or <bin-unit> elements. + + + + + Refers to the occurrences of instances defined by the count-type value. + + + + + Refers to characters. + + + + + Refers to lines. + + + + + Refers to sentences. + + + + + Refers to paragraphs. + + + + + Refers to segments. + + + + + Refers to placeables (inline elements). + + + + + + + Values for the attribute 'priority'. + + + + + Highest priority. + + + + + High priority. + + + + + High priority, but not as important as 2. + + + + + High priority, but not as important as 3. + + + + + Medium priority, but more important than 6. + + + + + Medium priority, but less important than 5. + + + + + Low priority, but more important than 8. + + + + + Low priority, but more important than 9. + + + + + Low priority. + + + + + Lowest priority. + + + + + + + + + This value indicates that all properties can be reformatted. This value must be used alone. + + + + + This value indicates that no properties should be reformatted. This value must be used alone. + + + + + + + + + + + + + This value indicates that all information in the coord attribute can be modified. + + + + + This value indicates that the x information in the coord attribute can be modified. + + + + + This value indicates that the y information in the coord attribute can be modified. + + + + + This value indicates that the cx information in the coord attribute can be modified. + + + + + This value indicates that the cy information in the coord attribute can be modified. + + + + + This value indicates that all the information in the font attribute can be modified. + + + + + This value indicates that the name information in the font attribute can be modified. + + + + + This value indicates that the size information in the font attribute can be modified. + + + + + This value indicates that the weight information in the font attribute can be modified. + + + + + This value indicates that the information in the css-style attribute can be modified. + + + + + This value indicates that the information in the style attribute can be modified. + + + + + This value indicates that the information in the exstyle attribute can be modified. + + + + + + + + + + + + + Indicates that the context is informational in nature, specifying for example, how a term should be translated. Thus, should be displayed to anyone editing the XLIFF document. + + + + + Indicates that the context-group is used to specify where the term was found in the translatable source. Thus, it is not displayed. + + + + + Indicates that the context information should be used during translation memory lookups. Thus, it is not displayed. + + + + + + + + + Represents a translation proposal from a translation memory or other resource. + + + + + Represents a previous version of the target element. + + + + + Represents a rejected version of the target element. + + + + + Represents a translation to be used for reference purposes only, for example from a related product or a different language. + + + + + Represents a proposed translation that was used for the translation of the trans-unit, possibly modified. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Values for the attribute 'coord'. + + + + + + + + Version values: 1.0 and 1.1 are allowed for backward compatibility. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Resources/schemas/xliff-core-2.0.xsd b/classes/Utilities/Misc/Symfony/Component/Translation/Loader/schema/dic/xliff-core/xliff-core-2.0.xsd similarity index 100% rename from classes/Utilities/Misc/Symfony/Component/Translation/Resources/schemas/xliff-core-2.0.xsd rename to classes/Utilities/Misc/Symfony/Component/Translation/Loader/schema/dic/xliff-core/xliff-core-2.0.xsd diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Resources/schemas/xml.xsd b/classes/Utilities/Misc/Symfony/Component/Translation/Loader/schema/dic/xliff-core/xml.xsd similarity index 100% rename from classes/Utilities/Misc/Symfony/Component/Translation/Resources/schemas/xml.xsd rename to classes/Utilities/Misc/Symfony/Component/Translation/Loader/schema/dic/xliff-core/xml.xsd diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/LocaleAwareInterface.php b/classes/Utilities/Misc/Symfony/Component/Translation/LocaleAwareInterface.php deleted file mode 100755 index cdef0129..00000000 --- a/classes/Utilities/Misc/Symfony/Component/Translation/LocaleAwareInterface.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation; - -interface LocaleAwareInterface -{ - /** - * Sets the current locale. - * - * @param string $locale The locale - * - * @throws \InvalidArgumentException If the locale contains invalid characters - */ - public function setLocale(string $locale); - /** - * Returns the current locale. - * - * @return string The locale - */ - public function getLocale(); -} diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/LoggingTranslator.php b/classes/Utilities/Misc/Symfony/Component/Translation/LoggingTranslator.php index 55cc7e66..2a838a84 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/LoggingTranslator.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/LoggingTranslator.php @@ -12,13 +12,10 @@ use ILAB\MediaCloud\Utilities\Misc\Psr\Log\LoggerInterface; use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidArgumentException; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\LocaleAwareInterface; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\TranslatorInterface; /** * @author Abdellatif Ait boudad */ -class LoggingTranslator implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\TranslatorInterface, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorInterface, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorBagInterface +class LoggingTranslator implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorInterface, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorBagInterface { /** * @var TranslatorInterface|TranslatorBagInterface @@ -28,13 +25,10 @@ class LoggingTranslator implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Contr /** * @param TranslatorInterface $translator The translator must implement TranslatorBagInterface */ - public function __construct($translator, \ILAB\MediaCloud\Utilities\Misc\Psr\Log\LoggerInterface $logger) + public function __construct(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorInterface $translator, \ILAB\MediaCloud\Utilities\Misc\Psr\Log\LoggerInterface $logger) { - if (!$translator instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorInterface && !$translator instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\TranslatorInterface) { - throw new \TypeError(\sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, \ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); - } - if (!$translator instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorBagInterface || !$translator instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\LocaleAwareInterface) { - throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidArgumentException(\sprintf('The Translator "%s" must implement TranslatorInterface, TranslatorBagInterface and LocaleAwareInterface.', \get_class($translator))); + if (!$translator instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorBagInterface) { + throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidArgumentException(\sprintf('The Translator "%s" must implement TranslatorInterface and TranslatorBagInterface.', \get_class($translator))); } $this->translator = $translator; $this->logger = $logger; @@ -50,17 +44,10 @@ public function trans($id, array $parameters = [], $domain = null, $locale = nul } /** * {@inheritdoc} - * - * @deprecated since Symfony 4.2, use the trans() method instead with a %count% parameter */ public function transChoice($id, $number, array $parameters = [], $domain = null, $locale = null) { - @\trigger_error(\sprintf('The "%s()" method is deprecated since Symfony 4.2, use the trans() one instead with a "%%count%%" parameter.', __METHOD__), \E_USER_DEPRECATED); - if ($this->translator instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\TranslatorInterface) { - $trans = $this->translator->trans($id, ['%count%' => $number] + $parameters, $domain, $locale); - } else { - $trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale); - } + $trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale); $this->log($id, $domain, $locale); return $trans; } @@ -69,12 +56,7 @@ public function transChoice($id, $number, array $parameters = [], $domain = null */ public function setLocale($locale) { - $prev = $this->translator->getLocale(); $this->translator->setLocale($locale); - if ($prev === $locale) { - return; - } - $this->logger->debug(\sprintf('The locale of the translator has changed from "%s" to "%s".', $prev, $locale)); } /** * {@inheritdoc} @@ -107,12 +89,16 @@ public function getFallbackLocales() */ public function __call($method, $args) { - return $this->translator->{$method}(...$args); + return \call_user_func_array([$this->translator, $method], $args); } /** * Logs for missing translations. + * + * @param string $id + * @param string|null $domain + * @param string|null $locale */ - private function log(?string $id, ?string $domain, ?string $locale) + private function log($id, $domain, $locale) { if (null === $domain) { $domain = 'messages'; diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/MessageCatalogue.php b/classes/Utilities/Misc/Symfony/Component/Translation/MessageCatalogue.php index 2827a7c2..346877b3 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/MessageCatalogue.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/MessageCatalogue.php @@ -27,11 +27,8 @@ class MessageCatalogue implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Compon * @param string $locale The locale * @param array $messages An array of messages classified by domain */ - public function __construct(?string $locale, array $messages = []) + public function __construct($locale, array $messages = []) { - if (null === $locale) { - @\trigger_error(\sprintf('Passing "null" to the first argument of the "%s" method has been deprecated since Symfony 4.4 and will throw an error in 5.0.', __METHOD__), \E_USER_DEPRECATED); - } $this->locale = $locale; $this->messages = $messages; } @@ -47,35 +44,17 @@ public function getLocale() */ public function getDomains() { - $domains = []; - $suffixLength = \strlen(self::INTL_DOMAIN_SUFFIX); - foreach ($this->messages as $domain => $messages) { - if (\strlen($domain) > $suffixLength && \false !== ($i = \strpos($domain, self::INTL_DOMAIN_SUFFIX, -$suffixLength))) { - $domain = \substr($domain, 0, $i); - } - $domains[$domain] = $domain; - } - return \array_values($domains); + return \array_keys($this->messages); } /** * {@inheritdoc} */ public function all($domain = null) { - if (null !== $domain) { - return ($this->messages[$domain . self::INTL_DOMAIN_SUFFIX] ?? []) + ($this->messages[$domain] ?? []); - } - $allMessages = []; - $suffixLength = \strlen(self::INTL_DOMAIN_SUFFIX); - foreach ($this->messages as $domain => $messages) { - if (\strlen($domain) > $suffixLength && \false !== ($i = \strpos($domain, self::INTL_DOMAIN_SUFFIX, -$suffixLength))) { - $domain = \substr($domain, 0, $i); - $allMessages[$domain] = $messages + ($allMessages[$domain] ?? []); - } else { - $allMessages[$domain] = ($allMessages[$domain] ?? []) + $messages; - } + if (null === $domain) { + return $this->messages; } - return $allMessages; + return isset($this->messages[$domain]) ? $this->messages[$domain] : []; } /** * {@inheritdoc} @@ -89,7 +68,7 @@ public function set($id, $translation, $domain = 'messages') */ public function has($id, $domain = 'messages') { - if (isset($this->messages[$domain][$id]) || isset($this->messages[$domain . self::INTL_DOMAIN_SUFFIX][$id])) { + if (isset($this->messages[$domain][$id])) { return \true; } if (null !== $this->fallbackCatalogue) { @@ -102,16 +81,13 @@ public function has($id, $domain = 'messages') */ public function defines($id, $domain = 'messages') { - return isset($this->messages[$domain][$id]) || isset($this->messages[$domain . self::INTL_DOMAIN_SUFFIX][$id]); + return isset($this->messages[$domain][$id]); } /** * {@inheritdoc} */ public function get($id, $domain = 'messages') { - if (isset($this->messages[$domain . self::INTL_DOMAIN_SUFFIX][$id])) { - return $this->messages[$domain . self::INTL_DOMAIN_SUFFIX][$id]; - } if (isset($this->messages[$domain][$id])) { return $this->messages[$domain][$id]; } @@ -125,7 +101,7 @@ public function get($id, $domain = 'messages') */ public function replace($messages, $domain = 'messages') { - unset($this->messages[$domain], $this->messages[$domain . self::INTL_DOMAIN_SUFFIX]); + $this->messages[$domain] = []; $this->add($messages, $domain); } /** @@ -148,10 +124,6 @@ public function addCatalogue(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\T throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\LogicException(\sprintf('Cannot add a catalogue for locale "%s" as the current locale for this catalogue is "%s"', $catalogue->getLocale(), $this->locale)); } foreach ($catalogue->all() as $domain => $messages) { - if ($intlMessages = $catalogue->all($domain . self::INTL_DOMAIN_SUFFIX)) { - $this->add($intlMessages, $domain . self::INTL_DOMAIN_SUFFIX); - $messages = \array_diff_key($messages, $intlMessages); - } $this->add($messages, $domain); } foreach ($catalogue->getResources() as $resource) { diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/MessageCatalogueInterface.php b/classes/Utilities/Misc/Symfony/Component/Translation/MessageCatalogueInterface.php index 65f4a1e7..29960ead 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/MessageCatalogueInterface.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/MessageCatalogueInterface.php @@ -18,7 +18,6 @@ */ interface MessageCatalogueInterface { - const INTL_DOMAIN_SUFFIX = '+intl-icu'; /** * Gets the catalogue locale. * diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/MessageSelector.php b/classes/Utilities/Misc/Symfony/Component/Translation/MessageSelector.php index 28c20b19..2ebbd856 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/MessageSelector.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/MessageSelector.php @@ -10,15 +10,12 @@ */ namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation; -@\trigger_error(\sprintf('The "%s" class is deprecated since Symfony 4.2, use IdentityTranslator instead.', \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageSelector::class), \E_USER_DEPRECATED); use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidArgumentException; /** * MessageSelector. * * @author Fabien Potencier * @author Bernhard Schussek - * - * @deprecated since Symfony 4.2, use IdentityTranslator instead. */ class MessageSelector { @@ -40,9 +37,9 @@ class MessageSelector * The two methods can also be mixed: * {0} There are no apples|one: There is one apple|more: There are %count% apples * - * @param string $message The message being translated - * @param int|float $number The number of items represented for the message - * @param string $locale The locale to use for choosing + * @param string $message The message being translated + * @param int $number The number of items represented for the message + * @param string $locale The locale to use for choosing * * @return string * diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/PluralizationRules.php b/classes/Utilities/Misc/Symfony/Component/Translation/PluralizationRules.php index b71ee5c7..2caed6cd 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/PluralizationRules.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/PluralizationRules.php @@ -14,8 +14,6 @@ * Returns the plural rules for a given locale. * * @author Fabien Potencier - * - * @deprecated since Symfony 4.2, use IdentityTranslator instead */ class PluralizationRules { @@ -30,9 +28,6 @@ class PluralizationRules */ public static function get($number, $locale) { - if (3 > \func_num_args() || \func_get_arg(2)) { - @\trigger_error(\sprintf('The "%s" class is deprecated since Symfony 4.2.', __CLASS__), \E_USER_DEPRECATED); - } if ('pt_BR' === $locale) { // temporary set a locale for brazilian $locale = 'xbr'; @@ -41,7 +36,7 @@ public static function get($number, $locale) $locale = \substr($locale, 0, -\strlen(\strrchr($locale, '_'))); } if (isset(self::$rules[$locale])) { - $return = self::$rules[$locale]($number); + $return = \call_user_func(self::$rules[$locale], $number); if (!\is_int($return) || $return < 0) { return 0; } @@ -178,7 +173,6 @@ public static function get($number, $locale) */ public static function set(callable $rule, $locale) { - @\trigger_error(\sprintf('The "%s" class is deprecated since Symfony 4.2.', __CLASS__), \E_USER_DEPRECATED); if ('pt_BR' === $locale) { // temporary set a locale for brazilian $locale = 'xbr'; diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Resources/data/parents.json b/classes/Utilities/Misc/Symfony/Component/Translation/Resources/data/parents.json deleted file mode 100755 index 6e45d9f3..00000000 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Resources/data/parents.json +++ /dev/null @@ -1,136 +0,0 @@ -{ - "az_Cyrl": "root", - "bs_Cyrl": "root", - "en_150": "en_001", - "en_AG": "en_001", - "en_AI": "en_001", - "en_AT": "en_150", - "en_AU": "en_001", - "en_BB": "en_001", - "en_BE": "en_150", - "en_BM": "en_001", - "en_BS": "en_001", - "en_BW": "en_001", - "en_BZ": "en_001", - "en_CA": "en_001", - "en_CC": "en_001", - "en_CH": "en_150", - "en_CK": "en_001", - "en_CM": "en_001", - "en_CX": "en_001", - "en_CY": "en_001", - "en_DE": "en_150", - "en_DG": "en_001", - "en_DK": "en_150", - "en_DM": "en_001", - "en_ER": "en_001", - "en_FI": "en_150", - "en_FJ": "en_001", - "en_FK": "en_001", - "en_FM": "en_001", - "en_GB": "en_001", - "en_GD": "en_001", - "en_GG": "en_001", - "en_GH": "en_001", - "en_GI": "en_001", - "en_GM": "en_001", - "en_GY": "en_001", - "en_HK": "en_001", - "en_IE": "en_001", - "en_IL": "en_001", - "en_IM": "en_001", - "en_IN": "en_001", - "en_IO": "en_001", - "en_JE": "en_001", - "en_JM": "en_001", - "en_KE": "en_001", - "en_KI": "en_001", - "en_KN": "en_001", - "en_KY": "en_001", - "en_LC": "en_001", - "en_LR": "en_001", - "en_LS": "en_001", - "en_MG": "en_001", - "en_MO": "en_001", - "en_MS": "en_001", - "en_MT": "en_001", - "en_MU": "en_001", - "en_MW": "en_001", - "en_MY": "en_001", - "en_NA": "en_001", - "en_NF": "en_001", - "en_NG": "en_001", - "en_NL": "en_150", - "en_NR": "en_001", - "en_NU": "en_001", - "en_NZ": "en_001", - "en_PG": "en_001", - "en_PH": "en_001", - "en_PK": "en_001", - "en_PN": "en_001", - "en_PW": "en_001", - "en_RW": "en_001", - "en_SB": "en_001", - "en_SC": "en_001", - "en_SD": "en_001", - "en_SE": "en_150", - "en_SG": "en_001", - "en_SH": "en_001", - "en_SI": "en_150", - "en_SL": "en_001", - "en_SS": "en_001", - "en_SX": "en_001", - "en_SZ": "en_001", - "en_TC": "en_001", - "en_TK": "en_001", - "en_TO": "en_001", - "en_TT": "en_001", - "en_TV": "en_001", - "en_TZ": "en_001", - "en_UG": "en_001", - "en_VC": "en_001", - "en_VG": "en_001", - "en_VU": "en_001", - "en_WS": "en_001", - "en_ZA": "en_001", - "en_ZM": "en_001", - "en_ZW": "en_001", - "es_AR": "es_419", - "es_BO": "es_419", - "es_BR": "es_419", - "es_BZ": "es_419", - "es_CL": "es_419", - "es_CO": "es_419", - "es_CR": "es_419", - "es_CU": "es_419", - "es_DO": "es_419", - "es_EC": "es_419", - "es_GT": "es_419", - "es_HN": "es_419", - "es_MX": "es_419", - "es_NI": "es_419", - "es_PA": "es_419", - "es_PE": "es_419", - "es_PR": "es_419", - "es_PY": "es_419", - "es_SV": "es_419", - "es_US": "es_419", - "es_UY": "es_419", - "es_VE": "es_419", - "pa_Arab": "root", - "pt_AO": "pt_PT", - "pt_CH": "pt_PT", - "pt_CV": "pt_PT", - "pt_GQ": "pt_PT", - "pt_GW": "pt_PT", - "pt_LU": "pt_PT", - "pt_MO": "pt_PT", - "pt_MZ": "pt_PT", - "pt_ST": "pt_PT", - "pt_TL": "pt_PT", - "sr_Latn": "root", - "uz_Arab": "root", - "uz_Cyrl": "root", - "zh_Hant": "root", - "zh_Hant_MO": "zh_Hant_HK" -} diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Translator.php b/classes/Utilities/Misc/Symfony/Component/Translation/Translator.php index 081c271c..d5e75207 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Translator.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Translator.php @@ -18,16 +18,13 @@ use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\NotFoundResourceException; use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\RuntimeException; use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Formatter\ChoiceMessageFormatterInterface; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Formatter\IntlFormatterInterface; use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Formatter\MessageFormatter; use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Formatter\MessageFormatterInterface; use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Loader\LoaderInterface; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\TranslatorInterface; /** * @author Fabien Potencier */ -class Translator implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorInterface, \ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\TranslatorInterface, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorBagInterface +class Translator implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorInterface, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\TranslatorBagInterface { /** * @var MessageCatalogueInterface[] @@ -61,33 +58,30 @@ class Translator implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Tr * @var bool */ private $debug; - private $cacheVary; /** * @var ConfigCacheFactoryInterface|null */ private $configCacheFactory; /** - * @var array|null - */ - private $parentLocales; - private $hasIntlFormatter; - /** + * @param string $locale The locale + * @param MessageFormatterInterface|null $formatter The message formatter + * @param string|null $cacheDir The directory to use for the cache + * @param bool $debug Use cache in debug mode ? + * * @throws InvalidArgumentException If a locale contains invalid characters */ - public function __construct(?string $locale, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Formatter\MessageFormatterInterface $formatter = null, string $cacheDir = null, bool $debug = \false, array $cacheVary = []) + public function __construct($locale, $formatter = null, $cacheDir = null, $debug = \false) { - if (null === $locale) { - @\trigger_error(\sprintf('Passing "null" as the $locale argument to %s() is deprecated since Symfony 4.4.', __METHOD__), \E_USER_DEPRECATED); - } - $this->setLocale($locale, \false); - if (null === $formatter) { + $this->setLocale($locale); + if ($formatter instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageSelector) { + $formatter = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Formatter\MessageFormatter($formatter); + @\trigger_error(\sprintf('Passing a "%s" instance into the "%s()" method as a second argument is deprecated since Symfony 3.4 and will be removed in 4.0. Inject a "%s" implementation instead.', \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageSelector::class, __METHOD__, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Formatter\MessageFormatterInterface::class), \E_USER_DEPRECATED); + } elseif (null === $formatter) { $formatter = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Formatter\MessageFormatter(); } $this->formatter = $formatter; $this->cacheDir = $cacheDir; $this->debug = $debug; - $this->cacheVary = $cacheVary; - $this->hasIntlFormatter = $formatter instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Formatter\IntlFormatterInterface; } public function setConfigCacheFactory(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\ConfigCacheFactoryInterface $configCacheFactory) { @@ -96,7 +90,8 @@ public function setConfigCacheFactory(\ILAB\MediaCloud\Utilities\Misc\Symfony\Co /** * Adds a Loader. * - * @param string $format The name of the loader (@see addResource()) + * @param string $format The name of the loader (@see addResource()) + * @param LoaderInterface $loader A LoaderInterface instance */ public function addLoader($format, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Loader\LoaderInterface $loader) { @@ -117,9 +112,6 @@ public function addResource($format, $resource, $locale, $domain = null) if (null === $domain) { $domain = 'messages'; } - if (null === $locale) { - @\trigger_error(\sprintf('Passing "null" to the third argument of the "%s" method has been deprecated since Symfony 4.4 and will throw an error in 5.0.', __METHOD__), \E_USER_DEPRECATED); - } $this->assertValidLocale($locale); $this->resources[$locale][] = [$format, $resource, $domain]; if (\in_array($locale, $this->fallbackLocales)) { @@ -133,9 +125,6 @@ public function addResource($format, $resource, $locale, $domain = null) */ public function setLocale($locale) { - if (null === $locale && (2 > \func_num_args() || \func_get_arg(1))) { - @\trigger_error(\sprintf('Passing "null" as the $locale argument to %s() is deprecated since Symfony 4.4.', __METHOD__), \E_USER_DEPRECATED); - } $this->assertValidLocale($locale); $this->locale = $locale; } @@ -158,18 +147,13 @@ public function setFallbackLocales(array $locales) // needed as the fallback locales are linked to the already loaded catalogues $this->catalogues = []; foreach ($locales as $locale) { - if (null === $locale) { - @\trigger_error(\sprintf('Passing "null" as the $locale argument to %s() is deprecated since Symfony 4.4.', __METHOD__), \E_USER_DEPRECATED); - } $this->assertValidLocale($locale); } - $this->fallbackLocales = $this->cacheVary['fallback_locales'] = $locales; + $this->fallbackLocales = $locales; } /** * Gets the fallback locales. * - * @internal since Symfony 4.2 - * * @return array The fallback locales */ public function getFallbackLocales() @@ -181,44 +165,23 @@ public function getFallbackLocales() */ public function trans($id, array $parameters = [], $domain = null, $locale = null) { - if ('' === ($id = (string) $id)) { - return ''; - } if (null === $domain) { $domain = 'messages'; } - $catalogue = $this->getCatalogue($locale); - $locale = $catalogue->getLocale(); - while (!$catalogue->defines($id, $domain)) { - if ($cat = $catalogue->getFallbackCatalogue()) { - $catalogue = $cat; - $locale = $catalogue->getLocale(); - } else { - break; - } - } - if ($this->hasIntlFormatter && $catalogue->defines($id, $domain . \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue::INTL_DOMAIN_SUFFIX)) { - return $this->formatter->formatIntl($catalogue->get($id, $domain), $locale, $parameters); - } - return $this->formatter->format($catalogue->get($id, $domain), $locale, $parameters); + return $this->formatter->format($this->getCatalogue($locale)->get((string) $id, $domain), $locale, $parameters); } /** * {@inheritdoc} - * - * @deprecated since Symfony 4.2, use the trans() method instead with a %count% parameter */ public function transChoice($id, $number, array $parameters = [], $domain = null, $locale = null) { - @\trigger_error(\sprintf('The "%s()" method is deprecated since Symfony 4.2, use the trans() one instead with a "%%count%%" parameter.', __METHOD__), \E_USER_DEPRECATED); - if ('' === ($id = (string) $id)) { - return ''; - } if (!$this->formatter instanceof \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Formatter\ChoiceMessageFormatterInterface) { throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\LogicException(\sprintf('The formatter "%s" does not support plural translations.', \get_class($this->formatter))); } if (null === $domain) { $domain = 'messages'; } + $id = (string) $id; $catalogue = $this->getCatalogue($locale); $locale = $catalogue->getLocale(); while (!$catalogue->defines($id, $domain)) { @@ -229,9 +192,6 @@ public function transChoice($id, $number, array $parameters = [], $domain = null break; } } - if ($this->hasIntlFormatter && $catalogue->defines($id, $domain . \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue::INTL_DOMAIN_SUFFIX)) { - return $this->formatter->formatIntl($catalogue->get($id, $domain), $locale, ['%count%' => $number] + $parameters); - } return $this->formatter->choiceFormat($catalogue->get($id, $domain), $number, $locale, $parameters); } /** @@ -284,7 +244,10 @@ protected function initializeCatalogue($locale) } $this->loadFallbackCatalogues($locale); } - private function initializeCacheCatalogue(string $locale) : void + /** + * @param string $locale + */ + private function initializeCacheCatalogue($locale) { if (isset($this->catalogues[$locale])) { /* Catalogue already initialized. */ @@ -301,7 +264,7 @@ private function initializeCacheCatalogue(string $locale) : void /* Read catalogue from cache. */ $this->catalogues[$locale] = (include $cache->getPath()); } - private function dumpCatalogue(string $locale, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\ConfigCacheInterface $cache) : void + private function dumpCatalogue($locale, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\ConfigCacheInterface $cache) { $this->initializeCatalogue($locale); $fallbackContent = $this->getFallbackContent($this->catalogues[$locale]); @@ -316,10 +279,10 @@ private function dumpCatalogue(string $locale, \ILAB\MediaCloud\Utilities\Misc\S return \$catalogue; EOF -, $locale, \var_export($this->getAllMessages($this->catalogues[$locale]), \true), $fallbackContent); +, $locale, \var_export($this->catalogues[$locale]->all(), \true), $fallbackContent); $cache->write($content, $this->catalogues[$locale]->getResources()); } - private function getFallbackContent(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue $catalogue) : string + private function getFallbackContent(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue $catalogue) { $fallbackContent = ''; $current = ''; @@ -334,20 +297,17 @@ private function getFallbackContent(\ILAB\MediaCloud\Utilities\Misc\Symfony\Comp $catalogue%s->addFallbackCatalogue($catalogue%s); EOF -, $fallbackSuffix, $fallback, \var_export($this->getAllMessages($fallbackCatalogue), \true), $currentSuffix, $fallbackSuffix); +, $fallbackSuffix, $fallback, \var_export($fallbackCatalogue->all(), \true), $currentSuffix, $fallbackSuffix); $current = $fallbackCatalogue->getLocale(); $fallbackCatalogue = $fallbackCatalogue->getFallbackCatalogue(); } return $fallbackContent; } - private function getCatalogueCachePath(string $locale) : string + private function getCatalogueCachePath($locale) { - return $this->cacheDir . '/catalogue.' . $locale . '.' . \strtr(\substr(\base64_encode(\hash('sha256', \serialize($this->cacheVary), \true)), 0, 7), '/', '_') . '.php'; + return $this->cacheDir . '/catalogue.' . $locale . '.' . \strtr(\substr(\base64_encode(\hash('sha256', \serialize($this->fallbackLocales), \true)), 0, 7), '/', '_') . '.php'; } - /** - * @internal - */ - protected function doLoadCatalogue(string $locale) : void + private function doLoadCatalogue($locale) { $this->catalogues[$locale] = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue($locale); if (isset($this->resources[$locale])) { @@ -359,14 +319,14 @@ protected function doLoadCatalogue(string $locale) : void } } } - private function loadFallbackCatalogues(string $locale) : void + private function loadFallbackCatalogues($locale) { $current = $this->catalogues[$locale]; foreach ($this->computeFallbackLocales($locale) as $fallback) { if (!isset($this->catalogues[$fallback])) { $this->initializeCatalogue($fallback); } - $fallbackCatalogue = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue($fallback, $this->getAllMessages($this->catalogues[$fallback])); + $fallbackCatalogue = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue($fallback, $this->catalogues[$fallback]->all()); foreach ($this->catalogues[$fallback]->getResources() as $resource) { $fallbackCatalogue->addResource($resource); } @@ -376,9 +336,6 @@ private function loadFallbackCatalogues(string $locale) : void } protected function computeFallbackLocales($locale) { - if (null === $this->parentLocales) { - $parentLocales = \json_decode(\file_get_contents(__DIR__ . '/Resources/data/parents.json'), \true); - } $locales = []; foreach ($this->fallbackLocales as $fallback) { if ($fallback === $locale) { @@ -386,18 +343,8 @@ protected function computeFallbackLocales($locale) } $locales[] = $fallback; } - while ($locale) { - $parent = $parentLocales[$locale] ?? null; - if (!$parent && \false !== \strrchr($locale, '_')) { - $locale = \substr($locale, 0, -\strlen(\strrchr($locale, '_'))); - } elseif ('root' !== $parent) { - $locale = $parent; - } else { - $locale = null; - } - if (null !== $locale) { - \array_unshift($locales, $locale); - } + if (\false !== \strrchr($locale, '_')) { + \array_unshift($locales, \substr($locale, 0, -\strlen(\strrchr($locale, '_')))); } return \array_unique($locales); } @@ -417,26 +364,14 @@ protected function assertValidLocale($locale) /** * Provides the ConfigCache factory implementation, falling back to a * default implementation if necessary. + * + * @return ConfigCacheFactoryInterface $configCacheFactory */ - private function getConfigCacheFactory() : \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\ConfigCacheFactoryInterface + private function getConfigCacheFactory() { if (!$this->configCacheFactory) { $this->configCacheFactory = new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Config\ConfigCacheFactory($this->debug); } return $this->configCacheFactory; } - private function getAllMessages(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogueInterface $catalogue) : array - { - $allMessages = []; - foreach ($catalogue->all() as $domain => $messages) { - if ($intlMessages = $catalogue->all($domain . \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue::INTL_DOMAIN_SUFFIX)) { - $allMessages[$domain . \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue::INTL_DOMAIN_SUFFIX] = $intlMessages; - $messages = \array_diff_key($messages, $intlMessages); - } - if ($messages) { - $allMessages[$domain] = $messages; - } - } - return $allMessages; - } } diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/TranslatorInterface.php b/classes/Utilities/Misc/Symfony/Component/Translation/TranslatorInterface.php index 7ed16616..c10765d7 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/TranslatorInterface.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/TranslatorInterface.php @@ -11,15 +11,12 @@ namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation; use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidArgumentException; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\LocaleAwareInterface; /** * TranslatorInterface. * * @author Fabien Potencier - * - * @deprecated since Symfony 4.2, use Symfony\Contracts\Translation\TranslatorInterface instead */ -interface TranslatorInterface extends \ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation\LocaleAwareInterface +interface TranslatorInterface { /** * Translates the given message. @@ -55,7 +52,7 @@ public function transChoice($id, $number, array $parameters = [], $domain = null * * @throws InvalidArgumentException If the locale contains invalid characters */ - public function setLocale(string $locale); + public function setLocale($locale); /** * Returns the current locale. * diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/TranslatorTrait.php b/classes/Utilities/Misc/Symfony/Component/Translation/TranslatorTrait.php deleted file mode 100755 index 79c0e9f0..00000000 --- a/classes/Utilities/Misc/Symfony/Component/Translation/TranslatorTrait.php +++ /dev/null @@ -1,222 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Contracts\Translation; - -use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidArgumentException; -/** - * A trait to help implement TranslatorInterface and LocaleAwareInterface. - * - * @author Fabien Potencier - */ -trait TranslatorTrait -{ - private $locale; - /** - * {@inheritdoc} - */ - public function setLocale(string $locale) - { - $this->locale = $locale; - } - /** - * {@inheritdoc} - */ - public function getLocale() - { - return $this->locale ?: \Locale::getDefault(); - } - /** - * {@inheritdoc} - */ - public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null) : string - { - if (null === $id || '' === $id) { - return ''; - } - if (!isset($parameters['%count%']) || !\is_numeric($parameters['%count%'])) { - return \strtr($id, $parameters); - } - $number = (float) $parameters['%count%']; - $locale = $locale ?: $this->getLocale(); - $parts = []; - if (\preg_match('/^\\|++$/', $id)) { - $parts = \explode('|', $id); - } elseif (\preg_match_all('/(?:\\|\\||[^\\|])++/', $id, $matches)) { - $parts = $matches[0]; - } - $intervalRegexp = <<<'EOF' -/^(?P - ({\s* - (\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*) - \s*}) - - | - - (?P[\[\]]) - \s* - (?P-Inf|\-?\d+(\.\d+)?) - \s*,\s* - (?P\+?Inf|\-?\d+(\.\d+)?) - \s* - (?P[\[\]]) -)\s*(?P.*?)$/xs -EOF; - $standardRules = []; - foreach ($parts as $part) { - $part = \trim(\str_replace('||', '|', $part)); - // try to match an explicit rule, then fallback to the standard ones - if (\preg_match($intervalRegexp, $part, $matches)) { - if ($matches[2]) { - foreach (\explode(',', $matches[3]) as $n) { - if ($number == $n) { - return \strtr($matches['message'], $parameters); - } - } - } else { - $leftNumber = '-Inf' === $matches['left'] ? -\INF : (float) $matches['left']; - $rightNumber = \is_numeric($matches['right']) ? (float) $matches['right'] : \INF; - if (('[' === $matches['left_delimiter'] ? $number >= $leftNumber : $number > $leftNumber) && (']' === $matches['right_delimiter'] ? $number <= $rightNumber : $number < $rightNumber)) { - return \strtr($matches['message'], $parameters); - } - } - } elseif (\preg_match('/^\\w+\\:\\s*(.*?)$/', $part, $matches)) { - $standardRules[] = $matches[1]; - } else { - $standardRules[] = $part; - } - } - $position = $this->getPluralizationRule($number, $locale); - if (!isset($standardRules[$position])) { - // when there's exactly one rule given, and that rule is a standard - // rule, use this rule - if (1 === \count($parts) && isset($standardRules[0])) { - return \strtr($standardRules[0], $parameters); - } - $message = \sprintf('Unable to choose a translation for "%s" with locale "%s" for value "%d". Double check that this translation has the correct plural options (e.g. "There is one apple|There are %%count%% apples").', $id, $locale, $number); - if (\class_exists(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidArgumentException::class)) { - throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidArgumentException($message); - } - throw new \InvalidArgumentException($message); - } - return \strtr($standardRules[$position], $parameters); - } - /** - * Returns the plural position to use for the given locale and number. - * - * The plural rules are derived from code of the Zend Framework (2010-09-25), - * which is subject to the new BSD license (http://framework.zend.com/license/new-bsd). - * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) - */ - private function getPluralizationRule(int $number, string $locale) : int - { - switch ('pt_BR' !== $locale && \strlen($locale) > 3 ? \substr($locale, 0, \strrpos($locale, '_')) : $locale) { - case 'af': - case 'bn': - case 'bg': - case 'ca': - case 'da': - case 'de': - case 'el': - case 'en': - case 'eo': - case 'es': - case 'et': - case 'eu': - case 'fa': - case 'fi': - case 'fo': - case 'fur': - case 'fy': - case 'gl': - case 'gu': - case 'ha': - case 'he': - case 'hu': - case 'is': - case 'it': - case 'ku': - case 'lb': - case 'ml': - case 'mn': - case 'mr': - case 'nah': - case 'nb': - case 'ne': - case 'nl': - case 'nn': - case 'no': - case 'oc': - case 'om': - case 'or': - case 'pa': - case 'pap': - case 'ps': - case 'pt': - case 'so': - case 'sq': - case 'sv': - case 'sw': - case 'ta': - case 'te': - case 'tk': - case 'ur': - case 'zu': - return 1 == $number ? 0 : 1; - case 'am': - case 'bh': - case 'fil': - case 'fr': - case 'gun': - case 'hi': - case 'hy': - case 'ln': - case 'mg': - case 'nso': - case 'pt_BR': - case 'ti': - case 'wa': - return 0 == $number || 1 == $number ? 0 : 1; - case 'be': - case 'bs': - case 'hr': - case 'ru': - case 'sh': - case 'sr': - case 'uk': - return 1 == $number % 10 && 11 != $number % 100 ? 0 : ($number % 10 >= 2 && $number % 10 <= 4 && ($number % 100 < 10 || $number % 100 >= 20) ? 1 : 2); - case 'cs': - case 'sk': - return 1 == $number ? 0 : ($number >= 2 && $number <= 4 ? 1 : 2); - case 'ga': - return 1 == $number ? 0 : (2 == $number ? 1 : 2); - case 'lt': - return 1 == $number % 10 && 11 != $number % 100 ? 0 : ($number % 10 >= 2 && ($number % 100 < 10 || $number % 100 >= 20) ? 1 : 2); - case 'sl': - return 1 == $number % 100 ? 0 : (2 == $number % 100 ? 1 : (3 == $number % 100 || 4 == $number % 100 ? 2 : 3)); - case 'mk': - return 1 == $number % 10 ? 0 : 1; - case 'mt': - return 1 == $number ? 0 : (0 == $number || $number % 100 > 1 && $number % 100 < 11 ? 1 : ($number % 100 > 10 && $number % 100 < 20 ? 2 : 3)); - case 'lv': - return 0 == $number ? 0 : (1 == $number % 10 && 11 != $number % 100 ? 1 : 2); - case 'pl': - return 1 == $number ? 0 : ($number % 10 >= 2 && $number % 10 <= 4 && ($number % 100 < 12 || $number % 100 > 14) ? 1 : 2); - case 'cy': - return 1 == $number ? 0 : (2 == $number ? 1 : (8 == $number || 11 == $number ? 2 : 3)); - case 'ro': - return 1 == $number ? 0 : (0 == $number || $number % 100 > 0 && $number % 100 < 20 ? 1 : 2); - case 'ar': - return 0 == $number ? 0 : (1 == $number ? 1 : (2 == $number ? 2 : ($number % 100 >= 3 && $number % 100 <= 10 ? 3 : ($number % 100 >= 11 && $number % 100 <= 99 ? 4 : 5)))); - default: - return 0; - } - } -} diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Util/XliffUtils.php b/classes/Utilities/Misc/Symfony/Component/Translation/Util/XliffUtils.php deleted file mode 100755 index ac3ae4d6..00000000 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Util/XliffUtils.php +++ /dev/null @@ -1,126 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Util; - -use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidArgumentException; -use ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidResourceException; -/** - * Provides some utility methods for XLIFF translation files, such as validating - * their contents according to the XSD schema. - * - * @author Fabien Potencier - */ -class XliffUtils -{ - /** - * Gets xliff file version based on the root "version" attribute. - * - * Defaults to 1.2 for backwards compatibility. - * - * @throws InvalidArgumentException - */ - public static function getVersionNumber(\DOMDocument $dom) : string - { - /** @var \DOMNode $xliff */ - foreach ($dom->getElementsByTagName('xliff') as $xliff) { - $version = $xliff->attributes->getNamedItem('version'); - if ($version) { - return $version->nodeValue; - } - $namespace = $xliff->attributes->getNamedItem('xmlns'); - if ($namespace) { - if (0 !== \substr_compare('urn:oasis:names:tc:xliff:document:', $namespace->nodeValue, 0, 34)) { - throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidArgumentException(\sprintf('Not a valid XLIFF namespace "%s"', $namespace)); - } - return \substr($namespace, 34); - } - } - // Falls back to v1.2 - return '1.2'; - } - /** - * Validates and parses the given file into a DOMDocument. - * - * @throws InvalidResourceException - */ - public static function validateSchema(\DOMDocument $dom) : array - { - $xliffVersion = static::getVersionNumber($dom); - $internalErrors = \libxml_use_internal_errors(\true); - $disableEntities = \libxml_disable_entity_loader(\false); - $isValid = @$dom->schemaValidateSource(self::getSchema($xliffVersion)); - if (!$isValid) { - \libxml_disable_entity_loader($disableEntities); - return self::getXmlErrors($internalErrors); - } - \libxml_disable_entity_loader($disableEntities); - $dom->normalizeDocument(); - \libxml_clear_errors(); - \libxml_use_internal_errors($internalErrors); - return []; - } - public static function getErrorsAsString(array $xmlErrors) : string - { - $errorsAsString = ''; - foreach ($xmlErrors as $error) { - $errorsAsString .= \sprintf("[%s %s] %s (in %s - line %d, column %d)\n", \LIBXML_ERR_WARNING === $error['level'] ? 'WARNING' : 'ERROR', $error['code'], $error['message'], $error['file'], $error['line'], $error['column']); - } - return $errorsAsString; - } - private static function getSchema(string $xliffVersion) : string - { - if ('1.2' === $xliffVersion) { - $schemaSource = \file_get_contents(__DIR__ . '/../Resources/schemas/xliff-core-1.2-strict.xsd'); - $xmlUri = 'http://www.w3.org/2001/xml.xsd'; - } elseif ('2.0' === $xliffVersion) { - $schemaSource = \file_get_contents(__DIR__ . '/../Resources/schemas/xliff-core-2.0.xsd'); - $xmlUri = 'informativeCopiesOf3rdPartySchemas/w3c/xml.xsd'; - } else { - throw new \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Exception\InvalidArgumentException(\sprintf('No support implemented for loading XLIFF version "%s".', $xliffVersion)); - } - return self::fixXmlLocation($schemaSource, $xmlUri); - } - /** - * Internally changes the URI of a dependent xsd to be loaded locally. - */ - private static function fixXmlLocation(string $schemaSource, string $xmlUri) : string - { - $newPath = \str_replace('\\', '/', __DIR__) . '/../Resources/schemas/xml.xsd'; - $parts = \explode('/', $newPath); - $locationstart = 'file:///'; - if (0 === \stripos($newPath, 'phar://')) { - $tmpfile = \tempnam(\sys_get_temp_dir(), 'symfony'); - if ($tmpfile) { - \copy($newPath, $tmpfile); - $parts = \explode('/', \str_replace('\\', '/', $tmpfile)); - } else { - \array_shift($parts); - $locationstart = 'phar:///'; - } - } - $drive = '\\' === \DIRECTORY_SEPARATOR ? \array_shift($parts) . '/' : ''; - $newPath = $locationstart . $drive . \implode('/', \array_map('rawurlencode', $parts)); - return \str_replace($xmlUri, $newPath, $schemaSource); - } - /** - * Returns the XML errors of the internal XML parser. - */ - private static function getXmlErrors(bool $internalErrors) : array - { - $errors = []; - foreach (\libxml_get_errors() as $error) { - $errors[] = ['level' => \LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR', 'code' => $error->code, 'message' => \trim($error->message), 'file' => $error->file ?: 'n/a', 'line' => $error->line, 'column' => $error->column]; - } - \libxml_clear_errors(); - \libxml_use_internal_errors($internalErrors); - return $errors; - } -} diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Writer/TranslationWriter.php b/classes/Utilities/Misc/Symfony/Component/Translation/Writer/TranslationWriter.php index 8fbccafe..e3e9aa10 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Writer/TranslationWriter.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Writer/TranslationWriter.php @@ -25,7 +25,8 @@ class TranslationWriter implements \ILAB\MediaCloud\Utilities\Misc\Symfony\Compo /** * Adds a dumper to the writer. * - * @param string $format The format of the dumper + * @param string $format The format of the dumper + * @param DumperInterface $dumper The dumper */ public function addDumper($format, \ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\Dumper\DumperInterface $dumper) { @@ -33,12 +34,9 @@ public function addDumper($format, \ILAB\MediaCloud\Utilities\Misc\Symfony\Compo } /** * Disables dumper backup. - * - * @deprecated since Symfony 4.1 */ public function disableBackup() { - @\trigger_error(\sprintf('The "%s()" method is deprecated since Symfony 4.1.', __METHOD__), \E_USER_DEPRECATED); foreach ($this->dumpers as $dumper) { if (\method_exists($dumper, 'setBackup')) { $dumper->setBackup(\false); @@ -57,8 +55,9 @@ public function getFormats() /** * Writes translation from the catalogue according to the selected format. * - * @param string $format The format to use to dump the messages - * @param array $options Options that are passed to the dumper + * @param MessageCatalogue $catalogue The message catalogue to write + * @param string $format The format to use to dump the messages + * @param array $options Options that are passed to the dumper * * @throws InvalidArgumentException */ @@ -75,4 +74,20 @@ public function write(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translat // save $dumper->dump($catalogue, $options); } + /** + * Writes translation from the catalogue according to the selected format. + * + * @param MessageCatalogue $catalogue The message catalogue to write + * @param string $format The format to use to dump the messages + * @param array $options Options that are passed to the dumper + * + * @throws InvalidArgumentException + * + * @deprecated since 3.4 will be removed in 4.0. Use write instead. + */ + public function writeTranslations(\ILAB\MediaCloud\Utilities\Misc\Symfony\Component\Translation\MessageCatalogue $catalogue, $format, $options = []) + { + @\trigger_error(\sprintf('The "%s()" method is deprecated since Symfony 3.4 and will be removed in 4.0. Use write() instead.', __METHOD__), \E_USER_DEPRECATED); + $this->write($catalogue, $format, $options); + } } diff --git a/classes/Utilities/Misc/Symfony/Component/Translation/Writer/TranslationWriterInterface.php b/classes/Utilities/Misc/Symfony/Component/Translation/Writer/TranslationWriterInterface.php index ed31b032..91ef4584 100755 --- a/classes/Utilities/Misc/Symfony/Component/Translation/Writer/TranslationWriterInterface.php +++ b/classes/Utilities/Misc/Symfony/Component/Translation/Writer/TranslationWriterInterface.php @@ -22,8 +22,9 @@ interface TranslationWriterInterface /** * Writes translation from the catalogue according to the selected format. * - * @param string $format The format to use to dump the messages - * @param array $options Options that are passed to the dumper + * @param MessageCatalogue $catalogue The message catalogue to write + * @param string $format The format to use to dump the messages + * @param array $options Options that are passed to the dumper * * @throws InvalidArgumentException */ diff --git a/composer.json b/composer.json index d49dbbdf..e4e86a33 100755 --- a/composer.json +++ b/composer.json @@ -14,26 +14,36 @@ ], "require" : { "php": ">=5.6", - "duncan3dc/blade": ">3.3", - "fasterimage/fasterimage": "^1.1", - "google/cloud-storage": "^1.10", - "google/cloud-vision": "^0.19.0", + "doctrine/inflector": "1.1.0", + "duncan3dc/blade": "3.4.0", + "fasterimage/fasterimage": "1.5.0", + "google/auth": "1.5.1", + "google/cloud-core": "1.29.0", + "google/cloud-storage": "1.12.3", + "google/cloud-vision": "0.19.3", + "google/protobuf": "3.9.0", + "grpc/grpc": "1.22.0", "guzzlehttp/guzzle": "6.3.3", - "ilab/b2-sdk-php": "^1.4", - "ilab/ilab-aws-media-cloud-sdk": "^0.3.2", - "ilab/ilab-flysystem-aws-s3-v3": "^0.2.2", - "imgix/imgix-php": "^1.0", - "ivopetkov/html5-dom-document-php": "^2.2", - "league/flysystem": "^1.0", - "league/glide": "^1.3", - "mikey179/vfsstream": "*", - "monolog/monolog": "^1.23", + "ilab/b2-sdk-php": "1.4.1", + "ilab/ilab-aws-media-cloud-sdk": "0.3.2", + "ilab/ilab-flysystem-aws-s3-v3": "0.2.2", + "imgix/imgix-php": "1.2.1", + "intervention/image": "2.5.0", + "ivopetkov/html5-dom-document-php": "2.0.2", + "league/flysystem": "1.0.53", + "league/glide": "1.5.0", + "mikey179/vfsstream": "1.6.6", + "monolog/monolog": "1.24.0", "psr/http-message-implementation": "*", - "ralouphie/mimey": "^2.1", - "smalot/pdfparser": "^0.13.2", - "superbalist/flysystem-google-storage": "^7.1", - "trntv/probe": "^1.0", - "zumba/amplitude-php": "^1.0" + "psr/log": "1.1.0", + "ralouphie/mimey": "2.1.0", + "smalot/pdfparser": "0.13.3", + "superbalist/flysystem-google-storage": "7.2.1", + "symfony/debug": "3.4.29", + "symfony/finder": "3.4.29", + "tecnickcom/tcpdf": "6.2.26", + "trntv/probe": "1.0.1", + "zumba/amplitude-php": "1.0.1" }, "autoload": { "psr-4": { diff --git a/config/storage.config.php b/config/storage.config.php index 8d4313a8..869865c9 100755 --- a/config/storage.config.php +++ b/config/storage.config.php @@ -31,6 +31,10 @@ "plugin" => "robin-image-optimizer/robin-image-optimizer.php", "description" => "The image optimization process is a black box with no available way for Media Cloud to hook into it. So while it will optimize your images, Media Cloud will be unaware that any optimizations occurred and will not transfer the result to cloud storage." ], + "TranslatePress - Multilingual" => [ + "plugin" => "translatepress-multilingual/index.php", + "description" => "There is a library conflict with TranslatePress that may prevent Media Cloud's background tasks from running. We are working on a compatibility fix for the next release of Media Cloud." + ], ], "badPlugins" => [ "OptiMole" => [ @@ -257,6 +261,18 @@ ] ] ], + "ilab-media-cloud-performance-settings" => [ + "title" => "Performance Settings", + "description" => "", + "options" => [ + "mcloud-storage-cache-lookups" => [ + "title" => "Cache Attachment Lookups", + "description" => "When this is enabled, Media Cloud will cache the results of any database queries it performs to map a URL to an attachment ID so that it can dynamically generate the correct URL. This should be left on as some of the queries that Media Cloud uses can be slow on sites with a large number of rows in the wp_post database table. But, if you are having problems, you can turn it off to restore the previous behavior.", + "type" => "checkbox", + "default" => true + ], + ] + ], "ilab-media-cloud-display-settings" => [ "title" => "Display Settings", "doc_link" => 'https://kb.mediacloud.press/articles/documentation/cloud-storage/media-library-integration', diff --git a/ilab-media-tools.php b/ilab-media-tools.php index 0dbb187f..a8b874f5 100755 --- a/ilab-media-tools.php +++ b/ilab-media-tools.php @@ -5,7 +5,7 @@ Plugin URI: https://github.com/interfacelab/ilab-media-tools Description: Automatically upload media to Amazon S3 and integrate with Imgix, a real-time image processing CDN. Boosts site performance and simplifies workflows. Author: interfacelab -Version: 3.3.11 +Version: 3.3.12 Author URI: http://interfacelab.io */ // Copyright (c) 2016 Interfacelab LLC. All rights reserved. @@ -93,7 +93,7 @@ } // Version Defines -define( 'MEDIA_CLOUD_VERSION', '3.3.11' ); +define( 'MEDIA_CLOUD_VERSION', '3.3.12' ); define( 'MEDIA_CLOUD_INFO_VERSION', '1.0.0' ); // Directory defines define( 'ILAB_TOOLS_DIR', dirname( __FILE__ ) ); diff --git a/public/js/ilab-media-direct-upload.js b/public/js/ilab-media-direct-upload.js index 8bda1cc8..1b7d9250 100755 --- a/public/js/ilab-media-direct-upload.js +++ b/public/js/ilab-media-direct-upload.js @@ -1 +1 @@ -!function(e){var t={};function r(n){if(t[n])return t[n].exports;var i=t[n]={i:n,l:!1,exports:{}};return e[n].call(i.exports,i,i.exports,r),i.l=!0,i.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)r.d(n,i,function(t){return e[t]}.bind(null,i));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="/",r(r.s=185)}([,function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(6),i=(e,t,r,n)=>{if(t+r>e.length){if("function"!=typeof n)throw new Error("Buffer out of space and no valid flush() function found");return n(e,t),0}return t};t.UINT8={len:1,get:(e,t)=>e.readUInt8(t),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=0&&r<=255),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeUInt8(r,s),s-t+this.len}},t.UINT16_LE={len:2,get:(e,t)=>e.readUInt16LE(t),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=0&&r<=65535),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeUInt16LE(r,s),s-t+this.len}},t.UINT16_BE={len:2,get:(e,t)=>e.readUInt16BE(t),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=0&&r<=65535),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeUInt16BE(r,s),s-t+this.len}},t.UINT24_LE={len:3,get:(e,t)=>e.readUIntLE(t,3),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=0&&r<=16777215),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeUIntLE(r,s,3),s-t+this.len}},t.UINT24_BE={len:3,get:(e,t)=>e.readUIntBE(t,3),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=0&&r<=16777215),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeUIntBE(r,s,3),s-t+this.len}},t.UINT32_LE={len:4,get:(e,t)=>e.readUInt32LE(t),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=0&&r<=4294967295),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeUInt32LE(r,s),s-t+this.len}},t.UINT32_BE={len:4,get:(e,t)=>e.readUInt32BE(t),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=0&&r<=4294967295),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeUInt32BE(r,s),s-t+this.len}},t.INT8={len:1,get:(e,t)=>e.readInt8(t),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=-128&&r<=127),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeInt8(r,s),s-t+this.len}},t.INT16_BE={len:2,get:(e,t)=>e.readInt16BE(t),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=-32768&&r<=32767),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeInt16BE(r,s),s-t+this.len}},t.INT16_LE={len:2,get:(e,t)=>e.readInt16LE(t),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=-32768&&r<=32767),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeInt16LE(r,s),s-t+this.len}},t.INT24_LE={len:3,get:(e,t)=>e.readIntLE(t,3),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=-8388608&&r<=8388607),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeIntLE(r,s,3),s-t+this.len}},t.INT24_BE={len:3,get:(e,t)=>e.readIntBE(t,3),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=-8388608&&r<=8388607),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeIntBE(r,s,3),s-t+this.len}},t.INT32_BE={len:4,get:(e,t)=>e.readInt32BE(t),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=-2147483648&&r<=2147483647),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeInt32BE(r,s),s-t+this.len}},t.INT32_LE={len:4,get:(e,t)=>e.readInt32LE(t),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=-2147483648&&r<=2147483647),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeInt32LE(r,s),s-t+this.len}},t.UINT64_LE={len:8,get(e,t){return function(e,t,r){r>>>=0;let n=e[t>>>=0],i=1,a=0;for(;++a>>=0;let i=1,a=0;e[r>>>=0]=255&t;for(;++a>>=0;let n=e[t>>>=0],i=1,a=0;for(;++a=(i*=128)&&(n-=Math.pow(2,8*r));return n}(e,t,this.len)},put(e,t,r){return s(e,r,t,this.len)}},t.UINT64_BE={len:8,get(e,t){return o(e,t,this.len)},put(e,t,r){return c(e,r,t,this.len)}},t.INT64_BE={len:8,get(e,t){return u(e,t,this.len)},put(e,t,r){return l(e,r,t,this.len)}};t.IgnoreType=class{constructor(e){this.len=e}get(e,t){return null}};t.BufferType=class{constructor(e){this.len=e}get(e,t){return e.slice(t,t+this.len)}};t.StringType=class{constructor(e,t){this.len=e,this.encoding=t}get(e,t){return e.toString(this.encoding,t,t+this.len)}};class a{constructor(e){this.len=e}static decode(e,t,r){let n="";for(let i=t;i>10),56320+(1023&e)))}static singleByteDecoder(e){if(a.inRange(e,0,127))return e;const t=a.windows1252[e-128];if(null===t)throw Error("invaliding encoding");return t}get(e,t=0){return a.decode(e,t,t+this.len)}}function s(e,t,r,n){t=+t;let i=0,a=1,s=0;for(e[r>>>=0]=255&t;++i>0)-s&255;return r+n}function o(e,t,r){r>>>=0;let n=e[(t>>>=0)+--r],i=1;for(;r>0&&(i*=256);)n+=e[t+--r]*i;return n}function c(e,t,r,n){t=+t;let i=(n>>>=0)-1,a=1;for(e[(r>>>=0)+i]=255&t;--i>=0&&(a*=256);)e[r+i]=t/a&255;return r+n}function u(e,t,r){let n=r>>>=0,i=1,a=e[(t>>>=0)+--n];for(;n>0&&(i*=256);)a+=e[t+--n]*i;return a>=(i*=128)&&(a-=Math.pow(2,8*r)),a}function l(e,t,r,n){t=+t;let i=n-1,a=1,s=0;for(e[(r>>>=0)+i]=255&t;--i>=0&&(a*=256);)t<0&&0===s&&0!==e[r+i+1]&&(s=1),e[r+i]=(t/a>>0)-s&255;return r+n}a.windows1252=[8364,129,8218,402,8222,8230,8224,8225,710,8240,352,8249,338,141,381,143,144,8216,8217,8220,8221,8226,8211,8212,732,8482,353,8250,339,157,382,376,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255],t.AnsiStringType=a,t.writeIntLE=s,t.readUIntBE=o,t.writeUIntBE=c,t.readIntBE=u,t.writeIntBE=l},function(e,t,r){(function(n){t.log=function(...e){return"object"==typeof console&&console.log&&console.log(...e)},t.formatArgs=function(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+e.exports.humanize(this.diff),!this.useColors)return;const r="color: "+this.color;t.splice(1,0,r,"color: inherit");let n=0,i=0;t[0].replace(/%[a-zA-Z%]/g,e=>{"%%"!==e&&(n++,"%c"===e&&(i=n))}),t.splice(i,0,r)},t.save=function(e){try{e?t.storage.setItem("debug",e):t.storage.removeItem("debug")}catch(e){}},t.load=function(){let e;try{e=t.storage.getItem("debug")}catch(e){}!e&&void 0!==n&&"env"in n&&(e=n.env.DEBUG);return e},t.useColors=function(){if("undefined"!=typeof window&&window.process&&("renderer"===window.process.type||window.process.__nwjs))return!0;if("undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))return!1;return"undefined"!=typeof document&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||"undefined"!=typeof window&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)},t.storage=function(){try{return localStorage}catch(e){}}(),t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"],e.exports=r(209)(t);const{formatters:i}=e.exports;i.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}}).call(this,r(9))},function(e,t,r){"use strict";(function(e){var n=r(187),i=r(188),a=r(34);function s(){return c.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function o(e,t){if(s()=s())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+s().toString(16)+" bytes");return 0|e}function h(e,t){if(c.isBuffer(e))return e.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(e)||e instanceof ArrayBuffer))return e.byteLength;"string"!=typeof e&&(e=""+e);var r=e.length;if(0===r)return 0;for(var n=!1;;)switch(t){case"ascii":case"latin1":case"binary":return r;case"utf8":case"utf-8":case void 0:return j(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*r;case"hex":return r>>>1;case"base64":return q(e).length;default:if(n)return j(e).length;t=(""+t).toLowerCase(),n=!0}}function m(e,t,r){var n=!1;if((void 0===t||t<0)&&(t=0),t>this.length)return"";if((void 0===r||r>this.length)&&(r=this.length),r<=0)return"";if((r>>>=0)<=(t>>>=0))return"";for(e||(e="utf8");;)switch(e){case"hex":return x(this,t,r);case"utf8":case"utf-8":return E(this,t,r);case"ascii":return F(this,t,r);case"latin1":case"binary":return A(this,t,r);case"base64":return C(this,t,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return P(this,t,r);default:if(n)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),n=!0}}function g(e,t,r){var n=e[t];e[t]=e[r],e[r]=n}function b(e,t,r,n,i){if(0===e.length)return-1;if("string"==typeof r?(n=r,r=0):r>2147483647?r=2147483647:r<-2147483648&&(r=-2147483648),r=+r,isNaN(r)&&(r=i?0:e.length-1),r<0&&(r=e.length+r),r>=e.length){if(i)return-1;r=e.length-1}else if(r<0){if(!i)return-1;r=0}if("string"==typeof t&&(t=c.from(t,n)),c.isBuffer(t))return 0===t.length?-1:y(e,t,r,n,i);if("number"==typeof t)return t&=255,c.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?i?Uint8Array.prototype.indexOf.call(e,t,r):Uint8Array.prototype.lastIndexOf.call(e,t,r):y(e,[t],r,n,i);throw new TypeError("val must be string, number or Buffer")}function y(e,t,r,n,i){var a,s=1,o=e.length,c=t.length;if(void 0!==n&&("ucs2"===(n=String(n).toLowerCase())||"ucs-2"===n||"utf16le"===n||"utf-16le"===n)){if(e.length<2||t.length<2)return-1;s=2,o/=2,c/=2,r/=2}function u(e,t){return 1===s?e[t]:e.readUInt16BE(t*s)}if(i){var l=-1;for(a=r;ao&&(r=o-c),a=r;a>=0;a--){for(var d=!0,f=0;fi&&(n=i):n=i;var a=t.length;if(a%2!=0)throw new TypeError("Invalid hex string");n>a/2&&(n=a/2);for(var s=0;s>8,i=r%256,a.push(i),a.push(n);return a}(t,e.length-r),e,r,n)}function C(e,t,r){return 0===t&&r===e.length?n.fromByteArray(e):n.fromByteArray(e.slice(t,r))}function E(e,t,r){r=Math.min(e.length,r);for(var n=[],i=t;i239?4:u>223?3:u>191?2:1;if(i+d<=r)switch(d){case 1:u<128&&(l=u);break;case 2:128==(192&(a=e[i+1]))&&(c=(31&u)<<6|63&a)>127&&(l=c);break;case 3:a=e[i+1],s=e[i+2],128==(192&a)&&128==(192&s)&&(c=(15&u)<<12|(63&a)<<6|63&s)>2047&&(c<55296||c>57343)&&(l=c);break;case 4:a=e[i+1],s=e[i+2],o=e[i+3],128==(192&a)&&128==(192&s)&&128==(192&o)&&(c=(15&u)<<18|(63&a)<<12|(63&s)<<6|63&o)>65535&&c<1114112&&(l=c)}null===l?(l=65533,d=1):l>65535&&(l-=65536,n.push(l>>>10&1023|55296),l=56320|1023&l),n.push(l),i+=d}return function(e){var t=e.length;if(t<=_)return String.fromCharCode.apply(String,e);var r="",n=0;for(;n0&&(e=this.toString("hex",0,r).match(/.{2}/g).join(" "),this.length>r&&(e+=" ... ")),""},c.prototype.compare=function(e,t,r,n,i){if(!c.isBuffer(e))throw new TypeError("Argument must be a Buffer");if(void 0===t&&(t=0),void 0===r&&(r=e?e.length:0),void 0===n&&(n=0),void 0===i&&(i=this.length),t<0||r>e.length||n<0||i>this.length)throw new RangeError("out of range index");if(n>=i&&t>=r)return 0;if(n>=i)return-1;if(t>=r)return 1;if(this===e)return 0;for(var a=(i>>>=0)-(n>>>=0),s=(r>>>=0)-(t>>>=0),o=Math.min(a,s),u=this.slice(n,i),l=e.slice(t,r),d=0;di)&&(r=i),e.length>0&&(r<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");n||(n="utf8");for(var a=!1;;)switch(n){case"hex":return w(this,e,t,r);case"utf8":case"utf-8":return T(this,e,t,r);case"ascii":return v(this,e,t,r);case"latin1":case"binary":return I(this,e,t,r);case"base64":return k(this,e,t,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return S(this,e,t,r);default:if(a)throw new TypeError("Unknown encoding: "+n);n=(""+n).toLowerCase(),a=!0}},c.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var _=4096;function F(e,t,r){var n="";r=Math.min(e.length,r);for(var i=t;in)&&(r=n);for(var i="",a=t;ar)throw new RangeError("Trying to access beyond buffer length")}function M(e,t,r,n,i,a){if(!c.isBuffer(e))throw new TypeError('"buffer" argument must be a Buffer instance');if(t>i||te.length)throw new RangeError("Index out of range")}function D(e,t,r,n){t<0&&(t=65535+t+1);for(var i=0,a=Math.min(e.length-r,2);i>>8*(n?i:1-i)}function O(e,t,r,n){t<0&&(t=4294967295+t+1);for(var i=0,a=Math.min(e.length-r,4);i>>8*(n?i:3-i)&255}function R(e,t,r,n,i,a){if(r+n>e.length)throw new RangeError("Index out of range");if(r<0)throw new RangeError("Index out of range")}function U(e,t,r,n,a){return a||R(e,0,r,4),i.write(e,t,r,n,23,4),r+4}function L(e,t,r,n,a){return a||R(e,0,r,8),i.write(e,t,r,n,52,8),r+8}c.prototype.slice=function(e,t){var r,n=this.length;if((e=~~e)<0?(e+=n)<0&&(e=0):e>n&&(e=n),(t=void 0===t?n:~~t)<0?(t+=n)<0&&(t=0):t>n&&(t=n),t0&&(i*=256);)n+=this[e+--t]*i;return n},c.prototype.readUInt8=function(e,t){return t||B(e,1,this.length),this[e]},c.prototype.readUInt16LE=function(e,t){return t||B(e,2,this.length),this[e]|this[e+1]<<8},c.prototype.readUInt16BE=function(e,t){return t||B(e,2,this.length),this[e]<<8|this[e+1]},c.prototype.readUInt32LE=function(e,t){return t||B(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},c.prototype.readUInt32BE=function(e,t){return t||B(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},c.prototype.readIntLE=function(e,t,r){e|=0,t|=0,r||B(e,t,this.length);for(var n=this[e],i=1,a=0;++a=(i*=128)&&(n-=Math.pow(2,8*t)),n},c.prototype.readIntBE=function(e,t,r){e|=0,t|=0,r||B(e,t,this.length);for(var n=t,i=1,a=this[e+--n];n>0&&(i*=256);)a+=this[e+--n]*i;return a>=(i*=128)&&(a-=Math.pow(2,8*t)),a},c.prototype.readInt8=function(e,t){return t||B(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},c.prototype.readInt16LE=function(e,t){t||B(e,2,this.length);var r=this[e]|this[e+1]<<8;return 32768&r?4294901760|r:r},c.prototype.readInt16BE=function(e,t){t||B(e,2,this.length);var r=this[e+1]|this[e]<<8;return 32768&r?4294901760|r:r},c.prototype.readInt32LE=function(e,t){return t||B(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},c.prototype.readInt32BE=function(e,t){return t||B(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},c.prototype.readFloatLE=function(e,t){return t||B(e,4,this.length),i.read(this,e,!0,23,4)},c.prototype.readFloatBE=function(e,t){return t||B(e,4,this.length),i.read(this,e,!1,23,4)},c.prototype.readDoubleLE=function(e,t){return t||B(e,8,this.length),i.read(this,e,!0,52,8)},c.prototype.readDoubleBE=function(e,t){return t||B(e,8,this.length),i.read(this,e,!1,52,8)},c.prototype.writeUIntLE=function(e,t,r,n){(e=+e,t|=0,r|=0,n)||M(this,e,t,r,Math.pow(2,8*r)-1,0);var i=1,a=0;for(this[t]=255&e;++a=0&&(a*=256);)this[t+i]=e/a&255;return t+r},c.prototype.writeUInt8=function(e,t,r){return e=+e,t|=0,r||M(this,e,t,1,255,0),c.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),this[t]=255&e,t+1},c.prototype.writeUInt16LE=function(e,t,r){return e=+e,t|=0,r||M(this,e,t,2,65535,0),c.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):D(this,e,t,!0),t+2},c.prototype.writeUInt16BE=function(e,t,r){return e=+e,t|=0,r||M(this,e,t,2,65535,0),c.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):D(this,e,t,!1),t+2},c.prototype.writeUInt32LE=function(e,t,r){return e=+e,t|=0,r||M(this,e,t,4,4294967295,0),c.TYPED_ARRAY_SUPPORT?(this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e):O(this,e,t,!0),t+4},c.prototype.writeUInt32BE=function(e,t,r){return e=+e,t|=0,r||M(this,e,t,4,4294967295,0),c.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):O(this,e,t,!1),t+4},c.prototype.writeIntLE=function(e,t,r,n){if(e=+e,t|=0,!n){var i=Math.pow(2,8*r-1);M(this,e,t,r,i-1,-i)}var a=0,s=1,o=0;for(this[t]=255&e;++a>0)-o&255;return t+r},c.prototype.writeIntBE=function(e,t,r,n){if(e=+e,t|=0,!n){var i=Math.pow(2,8*r-1);M(this,e,t,r,i-1,-i)}var a=r-1,s=1,o=0;for(this[t+a]=255&e;--a>=0&&(s*=256);)e<0&&0===o&&0!==this[t+a+1]&&(o=1),this[t+a]=(e/s>>0)-o&255;return t+r},c.prototype.writeInt8=function(e,t,r){return e=+e,t|=0,r||M(this,e,t,1,127,-128),c.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),e<0&&(e=255+e+1),this[t]=255&e,t+1},c.prototype.writeInt16LE=function(e,t,r){return e=+e,t|=0,r||M(this,e,t,2,32767,-32768),c.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):D(this,e,t,!0),t+2},c.prototype.writeInt16BE=function(e,t,r){return e=+e,t|=0,r||M(this,e,t,2,32767,-32768),c.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):D(this,e,t,!1),t+2},c.prototype.writeInt32LE=function(e,t,r){return e=+e,t|=0,r||M(this,e,t,4,2147483647,-2147483648),c.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24):O(this,e,t,!0),t+4},c.prototype.writeInt32BE=function(e,t,r){return e=+e,t|=0,r||M(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),c.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):O(this,e,t,!1),t+4},c.prototype.writeFloatLE=function(e,t,r){return U(this,e,t,!0,r)},c.prototype.writeFloatBE=function(e,t,r){return U(this,e,t,!1,r)},c.prototype.writeDoubleLE=function(e,t,r){return L(this,e,t,!0,r)},c.prototype.writeDoubleBE=function(e,t,r){return L(this,e,t,!1,r)},c.prototype.copy=function(e,t,r,n){if(r||(r=0),n||0===n||(n=this.length),t>=e.length&&(t=e.length),t||(t=0),n>0&&n=this.length)throw new RangeError("sourceStart out of bounds");if(n<0)throw new RangeError("sourceEnd out of bounds");n>this.length&&(n=this.length),e.length-t=0;--i)e[i+t]=this[i+r];else if(a<1e3||!c.TYPED_ARRAY_SUPPORT)for(i=0;i>>=0,r=void 0===r?this.length:r>>>0,e||(e=0),"number"==typeof e)for(a=t;a55295&&r<57344){if(!i){if(r>56319){(t-=3)>-1&&a.push(239,191,189);continue}if(s+1===n){(t-=3)>-1&&a.push(239,191,189);continue}i=r;continue}if(r<56320){(t-=3)>-1&&a.push(239,191,189),i=r;continue}r=65536+(i-55296<<10|r-56320)}else i&&(t-=3)>-1&&a.push(239,191,189);if(i=null,r<128){if((t-=1)<0)break;a.push(r)}else if(r<2048){if((t-=2)<0)break;a.push(r>>6|192,63&r|128)}else if(r<65536){if((t-=3)<0)break;a.push(r>>12|224,r>>6&63|128,63&r|128)}else{if(!(r<1114112))throw new Error("Invalid code point");if((t-=4)<0)break;a.push(r>>18|240,r>>12&63|128,r>>6&63|128,63&r|128)}}return a}function q(e){return n.toByteArray(function(e){if((e=function(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}(e).replace(z,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function H(e,t,r,n){for(var i=0;i=t.length||i>=e.length);++i)t[i+r]=e[i];return i}}).call(this,r(12))},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(6),i=r(215);class a{static findZero(e,t,r,n){let i=t;if("utf16"===n){for(;0!==e[i]||0!==e[i+1];){if(i>=r)return r;i+=2}return i}for(;0!==e[i];){if(i>=r)return r;i++}return i}static trimRightNull(e){const t=e.indexOf("\0");return-1===t?e:e.substr(0,t)}static swapBytes(e){const t=e.length;n.ok(0==(1&t),"Buffer length must be even");for(let r=0;r>i;const o=8-i,c=n-o;return c<0?s>>=8-i-n:c>0&&(s<<=c,s|=a.getBitAllignedNumber(e,t,r+o,c)),s}static isBitSet(e,t,r){return 1===a.getBitAllignedNumber(e,t,r,1)}static a2hex(e){const t=[];for(let r=0,n=e.length;r0!=(e[t]&1<e.trim().toLowerCase());if(t.length>=1){const e=parseFloat(t[0]);return 2===t.length&&"db"===t[1]?{dB:e,ratio:o(e)}:{dB:s(e),ratio:e}}}},function(e,t,r){"use strict";(function(e){Object.defineProperty(t,"__esModule",{value:!0});const n=r(4),i=/^[\w-©][\w-\x000-3]/;t.FourCcToken={len:4,get:(e,r)=>{const a=e.toString("binary",r,r+t.FourCcToken.len);if(!a.match(i))throw new Error(`FourCC contains invalid characters: ${n.default.a2hex(a)}`);return a},put:(t,r,n)=>{const i=e.from(n,"binary");if(4!==i.length)throw new Error("Invalid length");return i.copy(t,r)}}}).call(this,r(3).Buffer)},function(e,t,r){"use strict";(function(t){var n=r(196);function i(e,t){if(e===t)return 0;for(var r=e.length,n=t.length,i=0,a=Math.min(r,n);i=0;u--)if(l[u]!==d[u])return!1;for(u=l.length-1;u>=0;u--)if(o=l[u],!w(e[o],t[o],r,n))return!1;return!0}(e,t,r,n))}return r?e===t:e==t}function T(e){return"[object Arguments]"==Object.prototype.toString.call(e)}function v(e,t){if(!e||!t)return!1;if("[object RegExp]"==Object.prototype.toString.call(t))return t.test(e);try{if(e instanceof t)return!0}catch(e){}return!Error.isPrototypeOf(t)&&!0===t.call({},e)}function I(e,t,r,n){var i;if("function"!=typeof t)throw new TypeError('"block" argument must be a function');"string"==typeof r&&(n=r,r=null),i=function(e){var t;try{e()}catch(e){t=e}return t}(t),n=(r&&r.name?" ("+r.name+").":".")+(n?" "+n:"."),e&&!i&&b(i,r,"Missing expected exception"+n);var a="string"==typeof n,o=!e&&i&&!r;if((!e&&s.isError(i)&&a&&v(i,r)||o)&&b(i,r,"Got unwanted exception"+n),e&&i&&r&&!v(i,r)||!e&&i)throw i}f.AssertionError=function(e){this.name="AssertionError",this.actual=e.actual,this.expected=e.expected,this.operator=e.operator,e.message?(this.message=e.message,this.generatedMessage=!1):(this.message=function(e){return m(g(e.actual),128)+" "+e.operator+" "+m(g(e.expected),128)}(this),this.generatedMessage=!0);var t=e.stackStartFunction||b;if(Error.captureStackTrace)Error.captureStackTrace(this,t);else{var r=new Error;if(r.stack){var n=r.stack,i=h(t),a=n.indexOf("\n"+i);if(a>=0){var s=n.indexOf("\n",a+1);n=n.substring(s+1)}this.stack=n}}},s.inherits(f.AssertionError,Error),f.fail=b,f.ok=y,f.equal=function(e,t,r){e!=t&&b(e,t,r,"==",f.equal)},f.notEqual=function(e,t,r){e==t&&b(e,t,r,"!=",f.notEqual)},f.deepEqual=function(e,t,r){w(e,t,!1)||b(e,t,r,"deepEqual",f.deepEqual)},f.deepStrictEqual=function(e,t,r){w(e,t,!0)||b(e,t,r,"deepStrictEqual",f.deepStrictEqual)},f.notDeepEqual=function(e,t,r){w(e,t,!1)&&b(e,t,r,"notDeepEqual",f.notDeepEqual)},f.notDeepStrictEqual=function e(t,r,n){w(t,r,!0)&&b(t,r,n,"notDeepStrictEqual",e)},f.strictEqual=function(e,t,r){e!==t&&b(e,t,r,"===",f.strictEqual)},f.notStrictEqual=function(e,t,r){e===t&&b(e,t,r,"!==",f.notStrictEqual)},f.throws=function(e,t,r){I(!0,e,t,r)},f.doesNotThrow=function(e,t,r){I(!1,e,t,r)},f.ifError=function(e){if(e)throw e},f.strict=n(function e(t,r){t||b(t,!0,r,"==",e)},f,{equal:f.strictEqual,deepEqual:f.deepStrictEqual,notEqual:f.notStrictEqual,notDeepEqual:f.notDeepStrictEqual}),f.strict.strict=f.strict;var k=Object.keys||function(e){var t=[];for(var r in e)o.call(e,r)&&t.push(r);return t}}).call(this,r(12))},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.BasicParser=class{constructor(){this.warnings=[]}init(e,t,r){return this.metadata=e,this.tokenizer=t,this.options=r,this}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(25);class i{constructor(e,t){this.tagTypes=e,this.tagMap=t}static parseGenre(e){const t=e.trim().split(/\((.*?)\)/g).filter(e=>""!==e),r=[];for(let e of t)/^\d+$/.test(e)&&!isNaN(parseInt(e,10))&&(e=n.Genres[e]),r.push(e);return r.filter(e=>void 0!==e).join("/")}static fixPictureMimeType(e){switch(e=e.toLocaleLowerCase()){case"image/jpg":return"image/jpeg"}return e}static toIntOrNull(e){const t=parseInt(e,10);return isNaN(t)?null:t}static normalizeTrack(e){const t=e.toString().split("/");return{no:parseInt(t[0],10)||null,of:parseInt(t[1],10)||null}}mapGenericTag(e){e={id:e.id,value:e.value},this.postMap(e);const t=this.getCommonName(e.id);return t?{id:t,value:e.value}:null}getCommonName(e){return this.tagMap[e]}postMap(e){}}i.maxRatingScore=1,t.CommonTagMapper=i},function(e,t){var r,n,i=e.exports={};function a(){throw new Error("setTimeout has not been defined")}function s(){throw new Error("clearTimeout has not been defined")}function o(e){if(r===setTimeout)return setTimeout(e,0);if((r===a||!r)&&setTimeout)return r=setTimeout,setTimeout(e,0);try{return r(e,0)}catch(t){try{return r.call(null,e,0)}catch(t){return r.call(this,e,0)}}}!function(){try{r="function"==typeof setTimeout?setTimeout:a}catch(e){r=a}try{n="function"==typeof clearTimeout?clearTimeout:s}catch(e){n=s}}();var c,u=[],l=!1,d=-1;function f(){l&&c&&(l=!1,c.length?u=c.concat(u):d=-1,u.length&&p())}function p(){if(!l){var e=o(f);l=!0;for(var t=u.length;t;){for(c=u,u=[];++d1)for(var r=1;r0?this.parseExtendedHeaderData(t,e.size):this.parseId3Data(this.id3Header.size-e.size)}async parseExtendedHeaderData(t,r){const n=e.alloc(t);return await this.tokenizer.readBuffer(n,0,t),this.parseId3Data(this.id3Header.size-r)}async parseId3Data(t){const r=e.alloc(t);await this.tokenizer.readBuffer(r,0,t);for(const e of this.parseMetadata(r))if("TXXX"===e.id)for(const t of e.value.text)this.addTag(o.makeDescriptionTagName(e.id,e.value.description),t);else if("COM"===e.id)for(const t of e.value)this.addTag(o.makeDescriptionTagName(e.id,t.description),t.text);else if(Array.isArray(e.value))for(const t of e.value)this.addTag(e.id,t);else this.addTag(e.id,e.value)}addTag(e,t){this.metadata.addTag(this.headerType,e,t)}parseMetadata(e){let t=0;const r=[];for(;t!==e.length;){const n=o.getFrameHeaderLength(this.id3Header.version.major);if(t+n>e.length)break;const i=e.slice(t,t+=n),a=o.readFrameHeader(i,this.id3Header.version.major);if(""===a.id||"\0\0\0\0"===a.id||-1==="ABCDEFGHIJKLMNOPQRSTUVWXYZ".indexOf(a.id[0]))break;const s=e.slice(t,t+=a.length),c=o.readFrameData(s,a,this.id3Header.version.major,!this.options.skipCovers);r.push({id:a.id,value:c})}return r}}t.ID3v2Parser=o}).call(this,r(3).Buffer)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(4);!function(e){e[e.Other=0]="Other",e[e["32x32 pixels 'file icon' (PNG only)"]=1]="32x32 pixels 'file icon' (PNG only)",e[e["Other file icon"]=2]="Other file icon",e[e["Cover (front)"]=3]="Cover (front)",e[e["Cover (back)"]=4]="Cover (back)",e[e["Leaflet page"]=5]="Leaflet page",e[e["Media (e.g. label side of CD)"]=6]="Media (e.g. label side of CD)",e[e["Lead artist/lead performer/soloist"]=7]="Lead artist/lead performer/soloist",e[e["Artist/performer"]=8]="Artist/performer",e[e.Conductor=9]="Conductor",e[e["Band/Orchestra"]=10]="Band/Orchestra",e[e.Composer=11]="Composer",e[e["Lyricist/text writer"]=12]="Lyricist/text writer",e[e["Recording Location"]=13]="Recording Location",e[e["During recording"]=14]="During recording",e[e["During performance"]=15]="During performance",e[e["Movie/video screen capture"]=16]="Movie/video screen capture",e[e["A bright coloured fish"]=17]="A bright coloured fish",e[e.Illustration=18]="Illustration",e[e["Band/artist logotype"]=19]="Band/artist logotype",e[e["Publisher/Studio logotype"]=20]="Publisher/Studio logotype"}(t.AttachedPictureType||(t.AttachedPictureType={})),t.UINT32SYNCSAFE={get:(e,t)=>127&e[t+3]|e[t+2]<<7|e[t+1]<<14|e[t]<<21,len:4},t.ID3v2Header={len:10,get:(e,r)=>({fileIdentifier:new n.StringType(3,"ascii").get(e,r),version:{major:n.INT8.get(e,r+3),revision:n.INT8.get(e,r+4)},flags:{raw:n.INT8.get(e,r+4),unsynchronisation:i.default.strtokBITSET.get(e,r+5,7),isExtendedHeader:i.default.strtokBITSET.get(e,r+5,6),expIndicator:i.default.strtokBITSET.get(e,r+5,5),footer:i.default.strtokBITSET.get(e,r+5,4)},size:t.UINT32SYNCSAFE.get(e,r+6)})},t.ExtendedHeader={len:10,get:(e,t)=>({size:n.UINT32_BE.get(e,t),extendedFlags:n.UINT16_BE.get(e,t+4),sizeOfPadding:n.UINT32_BE.get(e,t+6),crcDataPresent:i.default.strtokBITSET.get(e,t+4,31)})},t.TextEncodingToken={len:1,get:(e,t)=>{switch(e.readUInt8(t)){case 0:return{encoding:"iso-8859-1"};case 1:return{encoding:"utf16",bom:!0};case 2:return{encoding:"utf16",bom:!1};case 3:default:return{encoding:"utf8",bom:!1}}}}},function(e,t,r){(function(e){function r(e){return Object.prototype.toString.call(e)}t.isArray=function(e){return Array.isArray?Array.isArray(e):"[object Array]"===r(e)},t.isBoolean=function(e){return"boolean"==typeof e},t.isNull=function(e){return null===e},t.isNullOrUndefined=function(e){return null==e},t.isNumber=function(e){return"number"==typeof e},t.isString=function(e){return"string"==typeof e},t.isSymbol=function(e){return"symbol"==typeof e},t.isUndefined=function(e){return void 0===e},t.isRegExp=function(e){return"[object RegExp]"===r(e)},t.isObject=function(e){return"object"==typeof e&&null!==e},t.isDate=function(e){return"[object Date]"===r(e)},t.isError=function(e){return"[object Error]"===r(e)||e instanceof Error},t.isFunction=function(e){return"function"==typeof e},t.isPrimitive=function(e){return null===e||"boolean"==typeof e||"number"==typeof e||"string"==typeof e||"symbol"==typeof e||void 0===e},t.isBuffer=e.isBuffer}).call(this,r(3).Buffer)},,function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(193),i=r(203);t.fromStream=function(e,t){return new n.ReadStreamTokenizer(e,t)},t.fromBuffer=function(e){return new i.BufferTokenizer(e)}},function(e,t,r){"use strict";(function(t){void 0===t||!t.version||0===t.version.indexOf("v0.")||0===t.version.indexOf("v1.")&&0!==t.version.indexOf("v1.8.")?e.exports={nextTick:function(e,r,n,i){if("function"!=typeof e)throw new TypeError('"callback" argument must be a function');var a,s,o=arguments.length;switch(o){case 0:case 1:return t.nextTick(e);case 2:return t.nextTick(function(){e.call(null,r)});case 3:return t.nextTick(function(){e.call(null,r,n)});case 4:return t.nextTick(function(){e.call(null,r,n,i)});default:for(a=new Array(o-1),s=0;s1?e.blocksPerFrame*(e.totalFrames-1):0;return(t+=e.finalFrameBlocks)/e.sampleRate}static async parseTagHeader(t,r,n){if(r.fileSize&&r.fileSize-r.position0?this.parseDescriptorExpansion(t):this.parseHeader());return await this.tokenizer.readToken(new a.IgnoreType(r.forwardBytes)),p.parseTagHeader(this.metadata,this.tokenizer,this.options)}async parseDescriptorExpansion(e){return await this.tokenizer.readToken(new a.IgnoreType(e)),this.parseHeader()}async parseHeader(){const e=await this.tokenizer.readToken(u.Header);return this.metadata.setFormat("lossless",!0),this.metadata.setFormat("container","Monkey's Audio"),this.metadata.setFormat("bitsPerSample",e.bitsPerSample),this.metadata.setFormat("sampleRate",e.sampleRate),this.metadata.setFormat("numberOfChannels",e.channel),this.metadata.setFormat("duration",p.calculateDuration(e)),{forwardBytes:this.ape.descriptor.seekTableBytes+this.ape.descriptor.headerDataBytes+this.ape.descriptor.apeFrameDataBytes+this.ape.descriptor.terminatingDataBytes}}}t.APEv2Parser=p}).call(this,r(3).Buffer)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(10),i=r(18),a=r(17),s=r(25),o=r(2),c=r(7),u=o("music-metadata:parser:ID3");t.AbstractID3Parser=class extends c.BasicParser{constructor(){super(...arguments),this.id3parser=new a.ID3v2Parser}static async startsWithID3v2Header(e){return"ID3"===(await e.peekToken(i.ID3v2Header)).fileIdentifier}parse(){return this.parseID3v2().catch(e=>{if(e.message!==n.endOfFile)throw e})}finalize(){}async parseID3v2(){if(await this.tryReadId3v2Headers(),u("End of ID3v2 header, go to MPEG-parser: pos=%s",this.tokenizer.position),await this._parse(),this.options.skipPostHeaders&&this.metadata.hasAny())this.finalize();else{const e=new s.ID3v1Parser;await e.init(this.metadata,this.tokenizer,this.options).parse(),this.finalize()}}async tryReadId3v2Headers(){if("ID3"===(await this.tokenizer.peekToken(i.ID3v2Header)).fileIdentifier)return u("Found ID3v2 header, pos=%s",this.tokenizer.position),await this.id3parser.parse(this.metadata,this.tokenizer,this.options),this.tryReadId3v2Headers()}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(2),i=r(4),a=r(1),s=r(7),o=n("music-metadata:parser:ID3v1");t.Genres=["Blues","Classic Rock","Country","Dance","Disco","Funk","Grunge","Hip-Hop","Jazz","Metal","New Age","Oldies","Other","Pop","R&B","Rap","Reggae","Rock","Techno","Industrial","Alternative","Ska","Death Metal","Pranks","Soundtrack","Euro-Techno","Ambient","Trip-Hop","Vocal","Jazz+Funk","Fusion","Trance","Classical","Instrumental","Acid","House","Game","Sound Clip","Gospel","Noise","Alt. Rock","Bass","Soul","Punk","Space","Meditative","Instrumental Pop","Instrumental Rock","Ethnic","Gothic","Darkwave","Techno-Industrial","Electronic","Pop-Folk","Eurodance","Dream","Southern Rock","Comedy","Cult","Gangsta Rap","Top 40","Christian Rap","Pop/Funk","Jungle","Native American","Cabaret","New Wave","Psychedelic","Rave","Showtunes","Trailer","Lo-Fi","Tribal","Acid Punk","Acid Jazz","Polka","Retro","Musical","Rock & Roll","Hard Rock","Folk","Folk/Rock","National Folk","Swing","Fast-Fusion","Bebob","Latin","Revival","Celtic","Bluegrass","Avantgarde","Gothic Rock","Progressive Rock","Psychedelic Rock","Symphonic Rock","Slow Rock","Big Band","Chorus","Easy Listening","Acoustic","Humour","Speech","Chanson","Opera","Chamber Music","Sonata","Symphony","Booty Bass","Primus","Porn Groove","Satire","Slow Jam","Club","Tango","Samba","Folklore","Ballad","Power Ballad","Rhythmic Soul","Freestyle","Duet","Punk Rock","Drum Solo","A Cappella","Euro-House","Dance Hall","Goa","Drum & Bass","Club-House","Hardcore","Terror","Indie","BritPop","Negerpunk","Polsk Punk","Beat","Christian Gangsta Rap","Heavy Metal","Black Metal","Crossover","Contemporary Christian","Christian Rock","Merengue","Salsa","Thrash Metal","Anime","JPop","Synthpop","Abstract","Art Rock","Baroque","Bhangra","Big Beat","Breakbeat","Chillout","Downtempo","Dub","EBM","Eclectic","Electro","Electroclash","Emo","Experimental","Garage","Global","IDM","Illbient","Industro-Goth","Jam Band","Krautrock","Leftfield","Lounge","Math Rock","New Romantic","Nu-Breakz","Post-Punk","Post-Rock","Psytrance","Shoegaze","Space Rock","Trop Rock","World Music","Neoclassical","Audiobook","Audio Theatre","Neue Deutsche Welle","Podcast","Indie Rock","G-Funk","Dubstep","Garage Rock","Psybient"];const c={len:128,get:(e,t)=>{const r=new u(3).get(e,t);return"TAG"===r?{header:r,title:new u(30).get(e,t+3),artist:new u(30).get(e,t+33),album:new u(30).get(e,t+63),year:new u(4).get(e,t+93),comment:new u(28).get(e,t+97),zeroByte:a.UINT8.get(e,t+127),track:a.UINT8.get(e,t+126),genre:a.UINT8.get(e,t+127)}:null}};class u extends a.StringType{constructor(e){super(e,"binary")}get(e,t){let r=super.get(e,t);return(r=(r=i.default.trimRightNull(r)).trim()).length>0?r:void 0}}class l extends s.BasicParser{static getGenre(e){if(ee)return void o("Already consumed the last 128 bytes");const t=await this.tokenizer.readToken(c,e);if(t){o("ID3v1 header found at: pos=%s",this.tokenizer.fileSize-c.len);for(const e of["title","artist","album","comment","track","year"])t[e]&&""!==t[e]&&this.addTag(e,t[e]);const e=l.getGenre(t.genre);e&&this.addTag("genre",e)}else o("ID3v1 header not found at: pos=%s",this.tokenizer.fileSize-c.len)}addTag(e,t){this.metadata.addTag("ID3v1",e,t)}}t.ID3v1Parser=l},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(36);t.ID3Stream=class extends n.Readable{constructor(e){super(),this.buf=e}_read(){this.push(this.buf),this.push(null)}}},function(e,t,r){"use strict";var n,i="object"==typeof Reflect?Reflect:null,a=i&&"function"==typeof i.apply?i.apply:function(e,t,r){return Function.prototype.apply.call(e,t,r)};n=i&&"function"==typeof i.ownKeys?i.ownKeys:Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:function(e){return Object.getOwnPropertyNames(e)};var s=Number.isNaN||function(e){return e!=e};function o(){o.init.call(this)}e.exports=o,o.EventEmitter=o,o.prototype._events=void 0,o.prototype._eventsCount=0,o.prototype._maxListeners=void 0;var c=10;function u(e){return void 0===e._maxListeners?o.defaultMaxListeners:e._maxListeners}function l(e,t,r,n){var i,a,s,o;if("function"!=typeof r)throw new TypeError('The "listener" argument must be of type Function. Received type '+typeof r);if(void 0===(a=e._events)?(a=e._events=Object.create(null),e._eventsCount=0):(void 0!==a.newListener&&(e.emit("newListener",t,r.listener?r.listener:r),a=e._events),s=a[t]),void 0===s)s=a[t]=r,++e._eventsCount;else if("function"==typeof s?s=a[t]=n?[r,s]:[s,r]:n?s.unshift(r):s.push(r),(i=u(e))>0&&s.length>i&&!s.warned){s.warned=!0;var c=new Error("Possible EventEmitter memory leak detected. "+s.length+" "+String(t)+" listeners added. Use emitter.setMaxListeners() to increase limit");c.name="MaxListenersExceededWarning",c.emitter=e,c.type=t,c.count=s.length,o=c,console&&console.warn&&console.warn(o)}return e}function d(e,t,r){var n={fired:!1,wrapFn:void 0,target:e,type:t,listener:r},i=function(){for(var e=[],t=0;t0&&(s=t[0]),s instanceof Error)throw s;var o=new Error("Unhandled error."+(s?" ("+s.message+")":""));throw o.context=s,o}var c=i[e];if(void 0===c)return!1;if("function"==typeof c)a(c,this,t);else{var u=c.length,l=h(c,u);for(r=0;r=0;a--)if(r[a]===t||r[a].listener===t){s=r[a].listener,i=a;break}if(i<0)return this;0===i?r.shift():function(e,t){for(;t+1=0;n--)this.removeListener(e,t[n]);return this},o.prototype.listeners=function(e){return f(this,e,!0)},o.prototype.rawListeners=function(e){return f(this,e,!1)},o.listenerCount=function(e,t){return"function"==typeof e.listenerCount?e.listenerCount(t):p.call(e,t)},o.prototype.listenerCount=p,o.prototype.eventNames=function(){return this._eventsCount>0?n(this._events):[]}},function(e,t,r){(t=e.exports=r(37)).Stream=t,t.Readable=t,t.Writable=r(30),t.Duplex=r(13),t.Transform=r(41),t.PassThrough=r(234)},function(e,t,r){var n=r(3),i=n.Buffer;function a(e,t){for(var r in e)t[r]=e[r]}function s(e,t,r){return i(e,t,r)}i.from&&i.alloc&&i.allocUnsafe&&i.allocUnsafeSlow?e.exports=n:(a(n,t),t.Buffer=s),a(i,s),s.from=function(e,t,r){if("number"==typeof e)throw new TypeError("Argument must not be a number");return i(e,t,r)},s.alloc=function(e,t,r){if("number"!=typeof e)throw new TypeError("Argument must be a number");var n=i(e);return void 0!==t?"string"==typeof r?n.fill(t,r):n.fill(t):n.fill(0),n},s.allocUnsafe=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return i(e)},s.allocUnsafeSlow=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return n.SlowBuffer(e)}},function(e,t,r){"use strict";(function(t,n,i){var a=r(22);function s(e){var t=this;this.next=null,this.entry=null,this.finish=function(){!function(e,t,r){var n=e.entry;e.entry=null;for(;n;){var i=n.callback;t.pendingcb--,i(r),n=n.next}t.corkedRequestsFree?t.corkedRequestsFree.next=e:t.corkedRequestsFree=e}(t,e)}}e.exports=y;var o,c=!t.browser&&["v0.10","v0.9."].indexOf(t.version.slice(0,5))>-1?n:a.nextTick;y.WritableState=b;var u=r(19);u.inherits=r(16);var l={deprecate:r(232)},d=r(38),f=r(29).Buffer,p=i.Uint8Array||function(){};var h,m=r(39);function g(){}function b(e,t){o=o||r(13),e=e||{};var n=t instanceof o;this.objectMode=!!e.objectMode,n&&(this.objectMode=this.objectMode||!!e.writableObjectMode);var i=e.highWaterMark,u=e.writableHighWaterMark,l=this.objectMode?16:16384;this.highWaterMark=i||0===i?i:n&&(u||0===u)?u:l,this.highWaterMark=Math.floor(this.highWaterMark),this.finalCalled=!1,this.needDrain=!1,this.ending=!1,this.ended=!1,this.finished=!1,this.destroyed=!1;var d=!1===e.decodeStrings;this.decodeStrings=!d,this.defaultEncoding=e.defaultEncoding||"utf8",this.length=0,this.writing=!1,this.corked=0,this.sync=!0,this.bufferProcessing=!1,this.onwrite=function(e){!function(e,t){var r=e._writableState,n=r.sync,i=r.writecb;if(function(e){e.writing=!1,e.writecb=null,e.length-=e.writelen,e.writelen=0}(r),t)!function(e,t,r,n,i){--t.pendingcb,r?(a.nextTick(i,n),a.nextTick(S,e,t),e._writableState.errorEmitted=!0,e.emit("error",n)):(i(n),e._writableState.errorEmitted=!0,e.emit("error",n),S(e,t))}(e,r,n,t,i);else{var s=I(r);s||r.corked||r.bufferProcessing||!r.bufferedRequest||v(e,r),n?c(T,e,r,s,i):T(e,r,s,i)}}(t,e)},this.writecb=null,this.writelen=0,this.bufferedRequest=null,this.lastBufferedRequest=null,this.pendingcb=0,this.prefinished=!1,this.errorEmitted=!1,this.bufferedRequestCount=0,this.corkedRequestsFree=new s(this)}function y(e){if(o=o||r(13),!(h.call(y,this)||this instanceof o))return new y(e);this._writableState=new b(e,this),this.writable=!0,e&&("function"==typeof e.write&&(this._write=e.write),"function"==typeof e.writev&&(this._writev=e.writev),"function"==typeof e.destroy&&(this._destroy=e.destroy),"function"==typeof e.final&&(this._final=e.final)),d.call(this)}function w(e,t,r,n,i,a,s){t.writelen=n,t.writecb=s,t.writing=!0,t.sync=!0,r?e._writev(i,t.onwrite):e._write(i,a,t.onwrite),t.sync=!1}function T(e,t,r,n){r||function(e,t){0===t.length&&t.needDrain&&(t.needDrain=!1,e.emit("drain"))}(e,t),t.pendingcb--,n(),S(e,t)}function v(e,t){t.bufferProcessing=!0;var r=t.bufferedRequest;if(e._writev&&r&&r.next){var n=t.bufferedRequestCount,i=new Array(n),a=t.corkedRequestsFree;a.entry=r;for(var o=0,c=!0;r;)i[o]=r,r.isBuf||(c=!1),r=r.next,o+=1;i.allBuffers=c,w(e,t,!0,t.length,i,"",a.finish),t.pendingcb++,t.lastBufferedRequest=null,a.next?(t.corkedRequestsFree=a.next,a.next=null):t.corkedRequestsFree=new s(t),t.bufferedRequestCount=0}else{for(;r;){var u=r.chunk,l=r.encoding,d=r.callback;if(w(e,t,!1,t.objectMode?1:u.length,u,l,d),r=r.next,t.bufferedRequestCount--,t.writing)break}null===r&&(t.lastBufferedRequest=null)}t.bufferedRequest=r,t.bufferProcessing=!1}function I(e){return e.ending&&0===e.length&&null===e.bufferedRequest&&!e.finished&&!e.writing}function k(e,t){e._final(function(r){t.pendingcb--,r&&e.emit("error",r),t.prefinished=!0,e.emit("prefinish"),S(e,t)})}function S(e,t){var r=I(t);return r&&(!function(e,t){t.prefinished||t.finalCalled||("function"==typeof e._final?(t.pendingcb++,t.finalCalled=!0,a.nextTick(k,e,t)):(t.prefinished=!0,e.emit("prefinish")))}(e,t),0===t.pendingcb&&(t.finished=!0,e.emit("finish"))),r}u.inherits(y,d),b.prototype.getBuffer=function(){for(var e=this.bufferedRequest,t=[];e;)t.push(e),e=e.next;return t},function(){try{Object.defineProperty(b.prototype,"buffer",{get:l.deprecate(function(){return this.getBuffer()},"_writableState.buffer is deprecated. Use _writableState.getBuffer instead.","DEP0003")})}catch(e){}}(),"function"==typeof Symbol&&Symbol.hasInstance&&"function"==typeof Function.prototype[Symbol.hasInstance]?(h=Function.prototype[Symbol.hasInstance],Object.defineProperty(y,Symbol.hasInstance,{value:function(e){return!!h.call(this,e)||this===y&&(e&&e._writableState instanceof b)}})):h=function(e){return e instanceof this},y.prototype.pipe=function(){this.emit("error",new Error("Cannot pipe, not readable"))},y.prototype.write=function(e,t,r){var n,i=this._writableState,s=!1,o=!i.objectMode&&(n=e,f.isBuffer(n)||n instanceof p);return o&&!f.isBuffer(e)&&(e=function(e){return f.from(e)}(e)),"function"==typeof t&&(r=t,t=null),o?t="buffer":t||(t=i.defaultEncoding),"function"!=typeof r&&(r=g),i.ended?function(e,t){var r=new Error("write after end");e.emit("error",r),a.nextTick(t,r)}(this,r):(o||function(e,t,r,n){var i=!0,s=!1;return null===r?s=new TypeError("May not write null values to stream"):"string"==typeof r||void 0===r||t.objectMode||(s=new TypeError("Invalid non-string/buffer chunk")),s&&(e.emit("error",s),a.nextTick(n,s),i=!1),i}(this,i,e,r))&&(i.pendingcb++,s=function(e,t,r,n,i,a){if(!r){var s=function(e,t,r){e.objectMode||!1===e.decodeStrings||"string"!=typeof t||(t=f.from(t,r));return t}(t,n,i);n!==s&&(r=!0,i="buffer",n=s)}var o=t.objectMode?1:n.length;t.length+=o;var c=t.length-1))throw new TypeError("Unknown encoding: "+e);return this._writableState.defaultEncoding=e,this},Object.defineProperty(y.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}}),y.prototype._write=function(e,t,r){r(new Error("_write() is not implemented"))},y.prototype._writev=null,y.prototype.end=function(e,t,r){var n=this._writableState;"function"==typeof e?(r=e,e=null,t=null):"function"==typeof t&&(r=t,t=null),null!=e&&this.write(e,t),n.corked&&(n.corked=1,this.uncork()),n.ending||n.finished||function(e,t,r){t.ending=!0,S(e,t),r&&(t.finished?a.nextTick(r):e.once("finish",r));t.ended=!0,e.writable=!1}(this,n,r)},Object.defineProperty(y.prototype,"destroyed",{get:function(){return void 0!==this._writableState&&this._writableState.destroyed},set:function(e){this._writableState&&(this._writableState.destroyed=e)}}),y.prototype.destroy=m.destroy,y.prototype._undestroy=m.undestroy,y.prototype._destroy=function(e,t){this.end(),t(e)}}).call(this,r(9),r(230).setImmediate,r(12))},function(e,t,r){"use strict";(function(e){Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(2),a=r(43),s=i("music-metadata:parser:ogg:vorbis1");t.VorbisParser=class{constructor(e,t){this.metadata=e,this.options=t,this.codecName="Vorbis I",this.pageSegments=[]}parsePage(t,r){if(t.headerType.firstPage)this.parseFirstPage(t,r);else{if(t.headerType.continued){if(0===this.pageSegments.length)throw new Error("Cannot continue on previous page");this.pageSegments.push(r)}if(t.headerType.lastPage||!t.headerType.continued){if(this.pageSegments.length>0){const t=e.concat(this.pageSegments);this.parseFullPage(t)}this.pageSegments=t.headerType.lastPage?[]:[r]}}t.headerType.lastPage&&this.calculateDuration(t)}flush(){this.parseFullPage(e.concat(this.pageSegments))}parseFirstPage(e,t){this.metadata.setFormat("codec","Vorbis I"),s("Parse first page");const r=a.CommonHeader.get(t,0);if("vorbis"!==r.vorbis)throw new Error("Metadata does not look like Vorbis");if(1!==r.packetType)throw new Error("First Ogg page should be type 1: the identification header");{const e=a.IdentificationHeader.get(t,a.CommonHeader.len);this.metadata.setFormat("sampleRate",e.sampleRate),this.metadata.setFormat("bitrate",e.bitrateNominal),this.metadata.setFormat("numberOfChannels",e.channelMode),s("sample-rate=%s[hz], bitrate=%s[b/s], channel-mode=%s",e.sampleRate,e.bitrateNominal,e.channelMode)}}parseFullPage(e){const t=a.CommonHeader.get(e,0);switch(s("Parse full page: type=%s, byteLength=%s",t.packetType,e.byteLength),t.packetType){case 3:return this.parseUserCommentList(e,a.CommonHeader.len)}}calculateDuration(e){this.metadata.format.sampleRate&&e.absoluteGranulePosition>=0&&(this.metadata.setFormat("numberOfSamples",e.absoluteGranulePosition),this.metadata.setFormat("duration",this.metadata.format.numberOfSamples/this.metadata.format.sampleRate))}parseUserCommentList(e,t){const r=n.UINT32_LE.get(e,t);t+=4,new n.StringType(r,"utf-8").get(e,t),t+=r;let i=n.UINT32_LE.get(e,t);for(t+=4;i-- >0;)t+=this.parseUserComment(e,t)}parseUserComment(e,t){const r=n.UINT32_LE.get(e,t),i=new n.StringType(r,"utf-8").get(e,t+4),o=i.indexOf("="),c=i.slice(0,o).toUpperCase();let u=i.slice(o+1);return"METADATA_BLOCK_PICTURE"===c&&(u=this.options.skipCovers?null:a.VorbisPictureToken.fromBase64(u)),null!==u&&(s("Push tag: id=%s, value=%s",c,u),this.metadata.addTag("vorbis",c,u)),n.UINT32_LE.len+r}}}).call(this,r(3).Buffer)},,,function(e,t){var r={}.toString;e.exports=Array.isArray||function(e){return"[object Array]"==r.call(e)}},function(module,exports,__webpack_require__){"use strict";(function(Buffer){const{multiByteIndexOf:multiByteIndexOf,stringToBytes:stringToBytes,readUInt64LE:readUInt64LE,tarHeaderChecksumMatches:tarHeaderChecksumMatches,uint8ArrayUtf8ByteString:uint8ArrayUtf8ByteString}=__webpack_require__(205),supported=__webpack_require__(206),xpiZipFilename=stringToBytes("META-INF/mozilla.rsa"),oxmlContentTypes=stringToBytes("[Content_Types].xml"),oxmlRels=stringToBytes("_rels/.rels"),fileType=e=>{if(!(e instanceof Uint8Array||e instanceof ArrayBuffer||Buffer.isBuffer(e)))throw new TypeError(`Expected the \`input\` argument to be of type \`Uint8Array\` or \`Buffer\` or \`ArrayBuffer\`, got \`${typeof e}\``);const t=e instanceof Uint8Array?e:new Uint8Array(e);if(!(t&&t.length>1))return;const r=(e,r)=>{r={offset:0,...r};for(let n=0;nr(stringToBytes(e),t);if(r([255,216,255]))return{ext:"jpg",mime:"image/jpeg"};if(r([137,80,78,71,13,10,26,10])){const e=33,r=t.findIndex((r,n)=>n>=e&&73===t[n]&&68===t[n+1]&&65===t[n+2]&&84===t[n+3]),n=t.subarray(e,r);return n.findIndex((e,t)=>97===n[t]&&99===n[t+1]&&84===n[t+2]&&76===n[t+3])>=0?{ext:"apng",mime:"image/apng"}:{ext:"png",mime:"image/png"}}if(r([71,73,70]))return{ext:"gif",mime:"image/gif"};if(r([87,69,66,80],{offset:8}))return{ext:"webp",mime:"image/webp"};if(r([70,76,73,70]))return{ext:"flif",mime:"image/flif"};if((r([73,73,42,0])||r([77,77,0,42]))&&r([67,82],{offset:8}))return{ext:"cr2",mime:"image/x-canon-cr2"};if(r([73,73,82,79,8,0,0,0,24]))return{ext:"orf",mime:"image/x-olympus-orf"};if(r([73,73,42,0])&&(r([16,251,134,1],{offset:4})||r([8,0,0,0,19,0],{offset:4})||r([8,0,0,0,18,0],{offset:4})))return{ext:"arw",mime:"image/x-sony-arw"};if(r([73,73,42,0,8,0,0,0])&&(r([45,0,254,0],{offset:8})||r([39,0,254,0],{offset:8})))return{ext:"dng",mime:"image/x-adobe-dng"};if(r([73,73,42,0])&&r([28,0,254,0],{offset:8}))return{ext:"nef",mime:"image/x-nikon-nef"};if(r([73,73,85,0,24,0,0,0,136,231,116,216]))return{ext:"rw2",mime:"image/x-panasonic-rw2"};if(n("FUJIFILMCCD-RAW"))return{ext:"raf",mime:"image/x-fujifilm-raf"};if(r([73,73,42,0])||r([77,77,0,42]))return{ext:"tif",mime:"image/tiff"};if(r([66,77]))return{ext:"bmp",mime:"image/bmp"};if(r([73,73,188]))return{ext:"jxr",mime:"image/vnd.ms-photo"};if(r([56,66,80,83]))return{ext:"psd",mime:"image/vnd.adobe.photoshop"};const i=[80,75,3,4];if(r(i)){if(r([109,105,109,101,116,121,112,101,97,112,112,108,105,99,97,116,105,111,110,47,101,112,117,98,43,122,105,112],{offset:30}))return{ext:"epub",mime:"application/epub+zip"};if(r(xpiZipFilename,{offset:30}))return{ext:"xpi",mime:"application/x-xpinstall"};if(n("mimetypeapplication/vnd.oasis.opendocument.text",{offset:30}))return{ext:"odt",mime:"application/vnd.oasis.opendocument.text"};if(n("mimetypeapplication/vnd.oasis.opendocument.spreadsheet",{offset:30}))return{ext:"ods",mime:"application/vnd.oasis.opendocument.spreadsheet"};if(n("mimetypeapplication/vnd.oasis.opendocument.presentation",{offset:30}))return{ext:"odp",mime:"application/vnd.oasis.opendocument.presentation"};let e,a=0,s=!1;do{const o=a+30;if(s||(s=r(oxmlContentTypes,{offset:o})||r(oxmlRels,{offset:o})),e||(n("word/",{offset:o})?e={ext:"docx",mime:"application/vnd.openxmlformats-officedocument.wordprocessingml.document"}:n("ppt/",{offset:o})?e={ext:"pptx",mime:"application/vnd.openxmlformats-officedocument.presentationml.presentation"}:n("xl/",{offset:o})&&(e={ext:"xlsx",mime:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"})),s&&e)return e;a=multiByteIndexOf(t,i,o)}while(a>=0);if(e)return e}if(r([80,75])&&(3===t[2]||5===t[2]||7===t[2])&&(4===t[3]||6===t[3]||8===t[3]))return{ext:"zip",mime:"application/zip"};if(r([48,48,48,48,48,48],{offset:148,mask:[248,248,248,248,248,248]})&&tarHeaderChecksumMatches(t))return{ext:"tar",mime:"application/x-tar"};if(r([82,97,114,33,26,7])&&(0===t[6]||1===t[6]))return{ext:"rar",mime:"application/x-rar-compressed"};if(r([31,139,8]))return{ext:"gz",mime:"application/gzip"};if(r([66,90,104]))return{ext:"bz2",mime:"application/x-bzip2"};if(r([55,122,188,175,39,28]))return{ext:"7z",mime:"application/x-7z-compressed"};if(r([120,1]))return{ext:"dmg",mime:"application/x-apple-diskimage"};if(r([102,114,101,101],{offset:4})||r([109,100,97,116],{offset:4})||r([109,111,111,118],{offset:4})||r([119,105,100,101],{offset:4}))return{ext:"mov",mime:"video/quicktime"};if(r([102,116,121,112],{offset:4})&&0!=(96&t[8])&&0!=(96&t[9])&&0!=(96&t[10])&&0!=(96&t[11])){const e=uint8ArrayUtf8ByteString(t,8,12);switch(e){case"mif1":return{ext:"heic",mime:"image/heif"};case"msf1":return{ext:"heic",mime:"image/heif-sequence"};case"heic":case"heix":return{ext:"heic",mime:"image/heic"};case"hevc":case"hevx":return{ext:"heic",mime:"image/heic-sequence"};case"qt ":return{ext:"mov",mime:"video/quicktime"};case"M4V ":case"M4VH":case"M4VP":return{ext:"m4v",mime:"video/x-m4v"};case"M4P ":return{ext:"m4p",mime:"video/mp4"};case"M4B ":return{ext:"m4b",mime:"audio/mp4"};case"M4A ":return{ext:"m4a",mime:"audio/x-m4a"};case"F4V ":return{ext:"f4v",mime:"video/mp4"};case"F4P ":return{ext:"f4p",mime:"video/mp4"};case"F4A ":return{ext:"f4a",mime:"audio/mp4"};case"F4B ":return{ext:"f4b",mime:"audio/mp4"};default:return e.startsWith("3g")?e.startsWith("3g2")?{ext:"3g2",mime:"video/3gpp2"}:{ext:"3gp",mime:"video/3gpp"}:{ext:"mp4",mime:"video/mp4"}}}if(r([77,84,104,100]))return{ext:"mid",mime:"audio/midi"};if(r([26,69,223,163])){const e=t.subarray(4,4100),r=e.findIndex((e,t,r)=>66===r[t]&&130===r[t+1]);if(-1!==r){const t=r+3,n=r=>[...r].every((r,n)=>e[t+n]===r.charCodeAt(0));if(n("matroska"))return{ext:"mkv",mime:"video/x-matroska"};if(n("webm"))return{ext:"webm",mime:"video/webm"}}}if(r([82,73,70,70])){if(r([65,86,73],{offset:8}))return{ext:"avi",mime:"video/vnd.avi"};if(r([87,65,86,69],{offset:8}))return{ext:"wav",mime:"audio/vnd.wave"};if(r([81,76,67,77],{offset:8}))return{ext:"qcp",mime:"audio/qcelp"}}if(r([48,38,178,117,142,102,207,17,166,217])){let e=30;do{const n=readUInt64LE(t,e+16);if(r([145,7,220,183,183,169,207,17,142,230,0,192,12,32,83,101],{offset:e})){if(r([64,158,105,248,77,91,207,17,168,253,0,128,95,92,68,43],{offset:e+24}))return{ext:"wma",mime:"audio/x-ms-wma"};if(r([192,239,25,188,77,91,207,17,168,253,0,128,95,92,68,43],{offset:e+24}))return{ext:"wmv",mime:"video/x-ms-asf"};break}e+=n}while(e+24<=t.length);return{ext:"asf",mime:"application/vnd.ms-asf"}}if(r([0,0,1,186])||r([0,0,1,179]))return{ext:"mpg",mime:"video/mpeg"};for(let e=0;e<2&&enew Promise((resolve,reject)=>{const stream=eval("require")("stream");readableStream.on("error",reject),readableStream.once("readable",()=>{const e=new stream.PassThrough,t=readableStream.read(module.exports.minimumBytes)||readableStream.read();try{e.fileType=fileType(t)}catch(e){reject(e)}readableStream.unshift(t),stream.pipeline?resolve(stream.pipeline(readableStream,e,()=>{})):resolve(readableStream.pipe(e))})}),Object.defineProperty(fileType,"extensions",{get:()=>new Set(supported.extensions)}),Object.defineProperty(fileType,"mimeTypes",{get:()=>new Set(supported.mimeTypes)})}).call(this,__webpack_require__(3).Buffer)},function(e,t,r){e.exports=i;var n=r(27).EventEmitter;function i(){n.call(this)}r(16)(i,n),i.Readable=r(28),i.Writable=r(235),i.Duplex=r(236),i.Transform=r(237),i.PassThrough=r(238),i.Stream=i,i.prototype.pipe=function(e,t){var r=this;function i(t){e.writable&&!1===e.write(t)&&r.pause&&r.pause()}function a(){r.readable&&r.resume&&r.resume()}r.on("data",i),e.on("drain",a),e._isStdio||t&&!1===t.end||(r.on("end",o),r.on("close",c));var s=!1;function o(){s||(s=!0,e.end())}function c(){s||(s=!0,"function"==typeof e.destroy&&e.destroy())}function u(e){if(l(),0===n.listenerCount(this,"error"))throw e}function l(){r.removeListener("data",i),e.removeListener("drain",a),r.removeListener("end",o),r.removeListener("close",c),r.removeListener("error",u),e.removeListener("error",u),r.removeListener("end",l),r.removeListener("close",l),e.removeListener("close",l)}return r.on("error",u),e.on("error",u),r.on("end",l),r.on("close",l),e.on("close",l),e.emit("pipe",r),e}},function(e,t,r){"use strict";(function(t,n){var i=r(22);e.exports=w;var a,s=r(34);w.ReadableState=y;r(27).EventEmitter;var o=function(e,t){return e.listeners(t).length},c=r(38),u=r(29).Buffer,l=t.Uint8Array||function(){};var d=r(19);d.inherits=r(16);var f=r(227),p=void 0;p=f&&f.debuglog?f.debuglog("stream"):function(){};var h,m=r(228),g=r(39);d.inherits(w,c);var b=["error","close","destroy","pause","resume"];function y(e,t){e=e||{};var n=t instanceof(a=a||r(13));this.objectMode=!!e.objectMode,n&&(this.objectMode=this.objectMode||!!e.readableObjectMode);var i=e.highWaterMark,s=e.readableHighWaterMark,o=this.objectMode?16:16384;this.highWaterMark=i||0===i?i:n&&(s||0===s)?s:o,this.highWaterMark=Math.floor(this.highWaterMark),this.buffer=new m,this.length=0,this.pipes=null,this.pipesCount=0,this.flowing=null,this.ended=!1,this.endEmitted=!1,this.reading=!1,this.sync=!0,this.needReadable=!1,this.emittedReadable=!1,this.readableListening=!1,this.resumeScheduled=!1,this.destroyed=!1,this.defaultEncoding=e.defaultEncoding||"utf8",this.awaitDrain=0,this.readingMore=!1,this.decoder=null,this.encoding=null,e.encoding&&(h||(h=r(40).StringDecoder),this.decoder=new h(e.encoding),this.encoding=e.encoding)}function w(e){if(a=a||r(13),!(this instanceof w))return new w(e);this._readableState=new y(e,this),this.readable=!0,e&&("function"==typeof e.read&&(this._read=e.read),"function"==typeof e.destroy&&(this._destroy=e.destroy)),c.call(this)}function T(e,t,r,n,i){var a,s=e._readableState;null===t?(s.reading=!1,function(e,t){if(t.ended)return;if(t.decoder){var r=t.decoder.end();r&&r.length&&(t.buffer.push(r),t.length+=t.objectMode?1:r.length)}t.ended=!0,S(e)}(e,s)):(i||(a=function(e,t){var r;n=t,u.isBuffer(n)||n instanceof l||"string"==typeof t||void 0===t||e.objectMode||(r=new TypeError("Invalid non-string/buffer chunk"));var n;return r}(s,t)),a?e.emit("error",a):s.objectMode||t&&t.length>0?("string"==typeof t||s.objectMode||Object.getPrototypeOf(t)===u.prototype||(t=function(e){return u.from(e)}(t)),n?s.endEmitted?e.emit("error",new Error("stream.unshift() after end event")):v(e,s,t,!0):s.ended?e.emit("error",new Error("stream.push() after EOF")):(s.reading=!1,s.decoder&&!r?(t=s.decoder.write(t),s.objectMode||0!==t.length?v(e,s,t,!1):E(e,s)):v(e,s,t,!1))):n||(s.reading=!1));return function(e){return!e.ended&&(e.needReadable||e.lengtht.highWaterMark&&(t.highWaterMark=function(e){return e>=I?e=I:(e--,e|=e>>>1,e|=e>>>2,e|=e>>>4,e|=e>>>8,e|=e>>>16,e++),e}(e)),e<=t.length?e:t.ended?t.length:(t.needReadable=!0,0))}function S(e){var t=e._readableState;t.needReadable=!1,t.emittedReadable||(p("emitReadable",t.flowing),t.emittedReadable=!0,t.sync?i.nextTick(C,e):C(e))}function C(e){p("emit readable"),e.emit("readable"),x(e)}function E(e,t){t.readingMore||(t.readingMore=!0,i.nextTick(_,e,t))}function _(e,t){for(var r=t.length;!t.reading&&!t.flowing&&!t.ended&&t.length=t.length?(r=t.decoder?t.buffer.join(""):1===t.buffer.length?t.buffer.head.data:t.buffer.concat(t.length),t.buffer.clear()):r=function(e,t,r){var n;ea.length?a.length:e;if(s===a.length?i+=a:i+=a.slice(0,e),0===(e-=s)){s===a.length?(++n,r.next?t.head=r.next:t.head=t.tail=null):(t.head=r,r.data=a.slice(s));break}++n}return t.length-=n,i}(e,t):function(e,t){var r=u.allocUnsafe(e),n=t.head,i=1;n.data.copy(r),e-=n.data.length;for(;n=n.next;){var a=n.data,s=e>a.length?a.length:e;if(a.copy(r,r.length-e,0,s),0===(e-=s)){s===a.length?(++i,n.next?t.head=n.next:t.head=t.tail=null):(t.head=n,n.data=a.slice(s));break}++i}return t.length-=i,r}(e,t);return n}(e,t.buffer,t.decoder),r);var r}function B(e){var t=e._readableState;if(t.length>0)throw new Error('"endReadable()" called on non-empty stream');t.endEmitted||(t.ended=!0,i.nextTick(M,t,e))}function M(e,t){e.endEmitted||0!==e.length||(e.endEmitted=!0,t.readable=!1,t.emit("end"))}function D(e,t){for(var r=0,n=e.length;r=t.highWaterMark||t.ended))return p("read: emitReadable",t.length,t.ended),0===t.length&&t.ended?B(this):S(this),null;if(0===(e=k(e,t))&&t.ended)return 0===t.length&&B(this),null;var n,i=t.needReadable;return p("need readable",i),(0===t.length||t.length-e0?P(e,t):null)?(t.needReadable=!0,e=0):t.length-=e,0===t.length&&(t.ended||(t.needReadable=!0),r!==e&&t.ended&&B(this)),null!==n&&this.emit("data",n),n},w.prototype._read=function(e){this.emit("error",new Error("_read() is not implemented"))},w.prototype.pipe=function(e,t){var r=this,a=this._readableState;switch(a.pipesCount){case 0:a.pipes=e;break;case 1:a.pipes=[a.pipes,e];break;default:a.pipes.push(e)}a.pipesCount+=1,p("pipe count=%d opts=%j",a.pipesCount,t);var c=(!t||!1!==t.end)&&e!==n.stdout&&e!==n.stderr?l:w;function u(t,n){p("onunpipe"),t===r&&n&&!1===n.hasUnpiped&&(n.hasUnpiped=!0,p("cleanup"),e.removeListener("close",b),e.removeListener("finish",y),e.removeListener("drain",d),e.removeListener("error",g),e.removeListener("unpipe",u),r.removeListener("end",l),r.removeListener("end",w),r.removeListener("data",m),f=!0,!a.awaitDrain||e._writableState&&!e._writableState.needDrain||d())}function l(){p("onend"),e.end()}a.endEmitted?i.nextTick(c):r.once("end",c),e.on("unpipe",u);var d=function(e){return function(){var t=e._readableState;p("pipeOnDrain",t.awaitDrain),t.awaitDrain&&t.awaitDrain--,0===t.awaitDrain&&o(e,"data")&&(t.flowing=!0,x(e))}}(r);e.on("drain",d);var f=!1;var h=!1;function m(t){p("ondata"),h=!1,!1!==e.write(t)||h||((1===a.pipesCount&&a.pipes===e||a.pipesCount>1&&-1!==D(a.pipes,e))&&!f&&(p("false write response, pause",r._readableState.awaitDrain),r._readableState.awaitDrain++,h=!0),r.pause())}function g(t){p("onerror",t),w(),e.removeListener("error",g),0===o(e,"error")&&e.emit("error",t)}function b(){e.removeListener("finish",y),w()}function y(){p("onfinish"),e.removeListener("close",b),w()}function w(){p("unpipe"),r.unpipe(e)}return r.on("data",m),function(e,t,r){if("function"==typeof e.prependListener)return e.prependListener(t,r);e._events&&e._events[t]?s(e._events[t])?e._events[t].unshift(r):e._events[t]=[r,e._events[t]]:e.on(t,r)}(e,"error",g),e.once("close",b),e.once("finish",y),e.emit("pipe",r),a.flowing||(p("pipe resume"),r.resume()),e},w.prototype.unpipe=function(e){var t=this._readableState,r={hasUnpiped:!1};if(0===t.pipesCount)return this;if(1===t.pipesCount)return e&&e!==t.pipes?this:(e||(e=t.pipes),t.pipes=null,t.pipesCount=0,t.flowing=!1,e&&e.emit("unpipe",this,r),this);if(!e){var n=t.pipes,i=t.pipesCount;t.pipes=null,t.pipesCount=0,t.flowing=!1;for(var a=0;a>5==6?2:e>>4==14?3:e>>3==30?4:e>>6==2?-1:-2}function o(e){var t=this.lastTotal-this.lastNeed,r=function(e,t,r){if(128!=(192&t[0]))return e.lastNeed=0,"�";if(e.lastNeed>1&&t.length>1){if(128!=(192&t[1]))return e.lastNeed=1,"�";if(e.lastNeed>2&&t.length>2&&128!=(192&t[2]))return e.lastNeed=2,"�"}}(this,e);return void 0!==r?r:this.lastNeed<=e.length?(e.copy(this.lastChar,t,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal)):(e.copy(this.lastChar,t,0,e.length),void(this.lastNeed-=e.length))}function c(e,t){if((e.length-t)%2==0){var r=e.toString("utf16le",t);if(r){var n=r.charCodeAt(r.length-1);if(n>=55296&&n<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1],r.slice(0,-1)}return r}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=e[e.length-1],e.toString("utf16le",t,e.length-1)}function u(e){var t=e&&e.length?this.write(e):"";if(this.lastNeed){var r=this.lastTotal-this.lastNeed;return t+this.lastChar.toString("utf16le",0,r)}return t}function l(e,t){var r=(e.length-t)%3;return 0===r?e.toString("base64",t):(this.lastNeed=3-r,this.lastTotal=3,1===r?this.lastChar[0]=e[e.length-1]:(this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1]),e.toString("base64",t,e.length-r))}function d(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+this.lastChar.toString("base64",0,3-this.lastNeed):t}function f(e){return e.toString(this.encoding)}function p(e){return e&&e.length?this.write(e):""}t.StringDecoder=a,a.prototype.write=function(e){if(0===e.length)return"";var t,r;if(this.lastNeed){if(void 0===(t=this.fillLast(e)))return"";r=this.lastNeed,this.lastNeed=0}else r=0;return r=0)return i>0&&(e.lastNeed=i-1),i;if(--n=0)return i>0&&(e.lastNeed=i-2),i;if(--n=0)return i>0&&(2===i?i=0:e.lastNeed=i-3),i;return 0}(this,e,t);if(!this.lastNeed)return e.toString("utf8",t);this.lastTotal=r;var n=e.length-(r-this.lastNeed);return e.copy(this.lastChar,0,n),e.toString("utf8",t,n)},a.prototype.fillLast=function(e){if(this.lastNeed<=e.length)return e.copy(this.lastChar,this.lastTotal-this.lastNeed,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal);e.copy(this.lastChar,this.lastTotal-this.lastNeed,0,e.length),this.lastNeed-=e.length}},function(e,t,r){"use strict";e.exports=s;var n=r(13),i=r(19);function a(e,t){var r=this._transformState;r.transforming=!1;var n=r.writecb;if(!n)return this.emit("error",new Error("write callback called multiple times"));r.writechunk=null,r.writecb=null,null!=t&&this.push(t),n(e);var i=this._readableState;i.reading=!1,(i.needReadable||i.length({packetType:e.readUInt8(t),vorbis:new n.StringType(6,"ascii").get(e,t+1)})},t.IdentificationHeader={len:23,get:(e,t)=>({version:e.readUInt32LE(t+0),channelMode:e.readUInt8(t+4),sampleRate:e.readUInt32LE(t+5),bitrateMax:e.readUInt32LE(t+9),bitrateNominal:e.readUInt32LE(t+13),bitrateMin:e.readUInt32LE(t+17)})}}).call(this,r(3).Buffer)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(5),a=r(2)("music-metadata:parser:MP4:atom");t.Header={len:8,get:(e,t)=>{const r=n.UINT32_BE.get(e,t);if(r<0)throw new Error("Invalid atom header length");return{length:r,name:i.FourCcToken.get(e,t+4)}},put:(e,t,r)=>(n.UINT32_BE.put(e,t,r.length),i.FourCcToken.put(e,t+4,r.name))},t.ExtendedSize=n.UINT64_BE,t.ftyp={len:4,get:(e,t)=>({type:new n.StringType(4,"ascii").get(e,t)})},t.mhdr={len:8,get:(e,t)=>({version:n.UINT8.get(e,t+0),flags:n.UINT24_BE.get(e,t+1),nextItemID:n.UINT32_BE.get(e,t+4)})};class s{constructor(e,t,r){if(this.len=e,et&&a(`Warning: atom ${r} expected to be ${t}, but was actually ${e} bytes long.`)}}t.FixedLengthAtom=s;t.MdhdAtom=class extends s{constructor(e){super(e,24,"mdhd"),this.len=e}get(e,t){return{version:n.UINT8.get(e,t+0),flags:n.UINT24_BE.get(e,t+1),creationTime:n.UINT32_BE.get(e,t+4),modificationTime:n.UINT32_BE.get(e,t+8),timeScale:n.UINT32_BE.get(e,t+12),duration:n.UINT32_BE.get(e,t+16),language:n.UINT16_BE.get(e,t+20),quality:n.UINT16_BE.get(e,t+22)}}};t.MvhdAtom=class extends s{constructor(e){super(e,100,"mvhd"),this.len=e}get(e,t){return{version:n.UINT8.get(e,t+0),flags:n.UINT24_BE.get(e,t+1),creationTime:n.UINT32_BE.get(e,t+4),modificationTime:n.UINT32_BE.get(e,t+8),timeScale:n.UINT32_BE.get(e,t+12),duration:n.UINT32_BE.get(e,t+16),preferredRate:n.UINT32_BE.get(e,t+20),preferredVolume:n.UINT16_BE.get(e,t+24),previewTime:n.UINT32_BE.get(e,t+72),previewDuration:n.UINT32_BE.get(e,t+76),posterTime:n.UINT32_BE.get(e,t+80),selectionTime:n.UINT32_BE.get(e,t+84),selectionDuration:n.UINT32_BE.get(e,t+88),currentTime:n.UINT32_BE.get(e,t+92),nextTrackID:n.UINT32_BE.get(e,t+96)}}};t.DataAtom=class{constructor(e){this.len=e}get(e,t){return{type:{set:n.UINT8.get(e,t+0),type:n.UINT24_BE.get(e,t+1)},locale:n.UINT24_BE.get(e,t+4),value:new n.BufferType(this.len-8).get(e,t+8)}}};t.NameAtom=class{constructor(e){this.len=e}get(e,t){return{version:n.UINT8.get(e,t),flags:n.UINT24_BE.get(e,t+1),name:new n.StringType(this.len-4,"utf-8").get(e,t+4)}}};t.TrackHeaderAtom=class{constructor(e){this.len=e}get(e,t){return{version:n.UINT8.get(e,t),flags:n.UINT24_BE.get(e,t+1),creationTime:n.UINT32_BE.get(e,t+4),modificationTime:n.UINT32_BE.get(e,t+8),trackId:n.UINT32_BE.get(e,t+12),duration:n.UINT32_BE.get(e,t+20),layer:n.UINT16_BE.get(e,t+24),alternateGroup:n.UINT16_BE.get(e,t+26),volume:n.UINT16_BE.get(e,t+28)}}};const o={len:8,get:(e,t)=>({version:n.UINT8.get(e,t+0),flags:n.UINT24_BE.get(e,t+1),numberOfEntries:n.UINT32_BE.get(e,t+4)})};class c{constructor(e){this.len=e}get(e,t){return{dataFormat:i.FourCcToken.get(e,t),dataReferenceIndex:n.UINT16_BE.get(e,t+10),description:new n.BufferType(this.len-12).get(e,t+12)}}}t.StsdAtom=class{constructor(e){this.len=e}get(e,t){const r=o.get(e,t);t+=o.len;const i=[];for(let a=0;a({version:n.INT16_BE.get(e,t),revision:n.INT16_BE.get(e,t+2),vendor:n.INT32_BE.get(e,t+4)})},t.SoundSampleDescriptionV0={len:12,get:(e,t)=>({numAudioChannels:n.INT16_BE.get(e,t+0),sampleSize:n.INT16_BE.get(e,t+2),compressionId:n.INT16_BE.get(e,t+4),packetSize:n.INT16_BE.get(e,t+6),sampleRate:n.UINT16_BE.get(e,t+8)+n.UINT16_BE.get(e,t+10)/1e4})}},,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(189),i=r(192),a=r(270),s=r(271),o=n("music-metadata-browser:main");t.parseNodeStream=i.parseStream,t.parseReadableStream=async function(e,r,n){const i=new a.ReadableWeToNodeStream(e),s=await t.parseNodeStream(i,r,n);return o(`Completed parsing from stream bytesRead=${i.bytesRead} / fileSize=${n&&n.fileSize?n.fileSize:"?"}`),await i.close(),s},t.parseBuffer=i.parseBuffer,t.parseBlob=function(e,t){return function(e){return new Promise((t,r)=>{const n=new FileReader;n.onloadend=e=>{let r=e.target.result;r instanceof ArrayBuffer&&(r=s(new Uint8Array(e.target.result))),t(r)},n.onerror=e=>{r(new Error(e.type))},n.onabort=e=>{r(new Error(e.type))},n.readAsArrayBuffer(e)})}(e).then(r=>i.parseBuffer(r,e.type,t))},t.fetchFromUrl=async function(e,t){const r=await fetch(e),n=r.headers.get("Content-Type"),i=[];if(r.headers.forEach(e=>{i.push(e)}),r.ok){if(r.body){const e=await this.parseReadableStream(r.body,n,t);return o("Closing HTTP-readable-stream..."),r.body.locked||await r.body.cancel(),o("HTTP-readable-stream closed."),e}return this.parseBlob(await r.blob(),t)}throw new Error(`HTTP error status=${r.status}: ${r.statusText}`)},t.parseFromTokenizer=i.parseFromTokenizer,t.orderTags=i.orderTags,t.ratingToStars=i.ratingToStars},function(e,t,r){var i;(function(){var r=!1,a=function(e){return e instanceof a?e:this instanceof a?void(this.EXIFwrapped=e):new a(e)};e.exports&&(t=e.exports=a),t.EXIF=a;var s=a.Tags={36864:"ExifVersion",40960:"FlashpixVersion",40961:"ColorSpace",40962:"PixelXDimension",40963:"PixelYDimension",37121:"ComponentsConfiguration",37122:"CompressedBitsPerPixel",37500:"MakerNote",37510:"UserComment",40964:"RelatedSoundFile",36867:"DateTimeOriginal",36868:"DateTimeDigitized",37520:"SubsecTime",37521:"SubsecTimeOriginal",37522:"SubsecTimeDigitized",33434:"ExposureTime",33437:"FNumber",34850:"ExposureProgram",34852:"SpectralSensitivity",34855:"ISOSpeedRatings",34856:"OECF",37377:"ShutterSpeedValue",37378:"ApertureValue",37379:"BrightnessValue",37380:"ExposureBias",37381:"MaxApertureValue",37382:"SubjectDistance",37383:"MeteringMode",37384:"LightSource",37385:"Flash",37396:"SubjectArea",37386:"FocalLength",41483:"FlashEnergy",41484:"SpatialFrequencyResponse",41486:"FocalPlaneXResolution",41487:"FocalPlaneYResolution",41488:"FocalPlaneResolutionUnit",41492:"SubjectLocation",41493:"ExposureIndex",41495:"SensingMethod",41728:"FileSource",41729:"SceneType",41730:"CFAPattern",41985:"CustomRendered",41986:"ExposureMode",41987:"WhiteBalance",41988:"DigitalZoomRation",41989:"FocalLengthIn35mmFilm",41990:"SceneCaptureType",41991:"GainControl",41992:"Contrast",41993:"Saturation",41994:"Sharpness",41995:"DeviceSettingDescription",41996:"SubjectDistanceRange",40965:"InteroperabilityIFDPointer",42016:"ImageUniqueID"},o=a.TiffTags={256:"ImageWidth",257:"ImageHeight",34665:"ExifIFDPointer",34853:"GPSInfoIFDPointer",40965:"InteroperabilityIFDPointer",258:"BitsPerSample",259:"Compression",262:"PhotometricInterpretation",274:"Orientation",277:"SamplesPerPixel",284:"PlanarConfiguration",530:"YCbCrSubSampling",531:"YCbCrPositioning",282:"XResolution",283:"YResolution",296:"ResolutionUnit",273:"StripOffsets",278:"RowsPerStrip",279:"StripByteCounts",513:"JPEGInterchangeFormat",514:"JPEGInterchangeFormatLength",301:"TransferFunction",318:"WhitePoint",319:"PrimaryChromaticities",529:"YCbCrCoefficients",532:"ReferenceBlackWhite",306:"DateTime",270:"ImageDescription",271:"Make",272:"Model",305:"Software",315:"Artist",33432:"Copyright"},c=a.GPSTags={0:"GPSVersionID",1:"GPSLatitudeRef",2:"GPSLatitude",3:"GPSLongitudeRef",4:"GPSLongitude",5:"GPSAltitudeRef",6:"GPSAltitude",7:"GPSTimeStamp",8:"GPSSatellites",9:"GPSStatus",10:"GPSMeasureMode",11:"GPSDOP",12:"GPSSpeedRef",13:"GPSSpeed",14:"GPSTrackRef",15:"GPSTrack",16:"GPSImgDirectionRef",17:"GPSImgDirection",18:"GPSMapDatum",19:"GPSDestLatitudeRef",20:"GPSDestLatitude",21:"GPSDestLongitudeRef",22:"GPSDestLongitude",23:"GPSDestBearingRef",24:"GPSDestBearing",25:"GPSDestDistanceRef",26:"GPSDestDistance",27:"GPSProcessingMethod",28:"GPSAreaInformation",29:"GPSDateStamp",30:"GPSDifferential"},u=a.IFD1Tags={256:"ImageWidth",257:"ImageHeight",258:"BitsPerSample",259:"Compression",262:"PhotometricInterpretation",273:"StripOffsets",274:"Orientation",277:"SamplesPerPixel",278:"RowsPerStrip",279:"StripByteCounts",282:"XResolution",283:"YResolution",284:"PlanarConfiguration",296:"ResolutionUnit",513:"JpegIFOffset",514:"JpegIFByteCount",529:"YCbCrCoefficients",530:"YCbCrSubSampling",531:"YCbCrPositioning",532:"ReferenceBlackWhite"},l=a.StringValues={ExposureProgram:{0:"Not defined",1:"Manual",2:"Normal program",3:"Aperture priority",4:"Shutter priority",5:"Creative program",6:"Action program",7:"Portrait mode",8:"Landscape mode"},MeteringMode:{0:"Unknown",1:"Average",2:"CenterWeightedAverage",3:"Spot",4:"MultiSpot",5:"Pattern",6:"Partial",255:"Other"},LightSource:{0:"Unknown",1:"Daylight",2:"Fluorescent",3:"Tungsten (incandescent light)",4:"Flash",9:"Fine weather",10:"Cloudy weather",11:"Shade",12:"Daylight fluorescent (D 5700 - 7100K)",13:"Day white fluorescent (N 4600 - 5400K)",14:"Cool white fluorescent (W 3900 - 4500K)",15:"White fluorescent (WW 3200 - 3700K)",17:"Standard light A",18:"Standard light B",19:"Standard light C",20:"D55",21:"D65",22:"D75",23:"D50",24:"ISO studio tungsten",255:"Other"},Flash:{0:"Flash did not fire",1:"Flash fired",5:"Strobe return light not detected",7:"Strobe return light detected",9:"Flash fired, compulsory flash mode",13:"Flash fired, compulsory flash mode, return light not detected",15:"Flash fired, compulsory flash mode, return light detected",16:"Flash did not fire, compulsory flash mode",24:"Flash did not fire, auto mode",25:"Flash fired, auto mode",29:"Flash fired, auto mode, return light not detected",31:"Flash fired, auto mode, return light detected",32:"No flash function",65:"Flash fired, red-eye reduction mode",69:"Flash fired, red-eye reduction mode, return light not detected",71:"Flash fired, red-eye reduction mode, return light detected",73:"Flash fired, compulsory flash mode, red-eye reduction mode",77:"Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",79:"Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",89:"Flash fired, auto mode, red-eye reduction mode",93:"Flash fired, auto mode, return light not detected, red-eye reduction mode",95:"Flash fired, auto mode, return light detected, red-eye reduction mode"},SensingMethod:{1:"Not defined",2:"One-chip color area sensor",3:"Two-chip color area sensor",4:"Three-chip color area sensor",5:"Color sequential area sensor",7:"Trilinear sensor",8:"Color sequential linear sensor"},SceneCaptureType:{0:"Standard",1:"Landscape",2:"Portrait",3:"Night scene"},SceneType:{1:"Directly photographed"},CustomRendered:{0:"Normal process",1:"Custom process"},WhiteBalance:{0:"Auto white balance",1:"Manual white balance"},GainControl:{0:"None",1:"Low gain up",2:"High gain up",3:"Low gain down",4:"High gain down"},Contrast:{0:"Normal",1:"Soft",2:"Hard"},Saturation:{0:"Normal",1:"Low saturation",2:"High saturation"},Sharpness:{0:"Normal",1:"Soft",2:"Hard"},SubjectDistanceRange:{0:"Unknown",1:"Macro",2:"Close view",3:"Distant view"},FileSource:{3:"DSC"},Components:{0:"",1:"Y",2:"Cb",3:"Cr",4:"R",5:"G",6:"B"}};function d(e){return!!e.exifdata}function f(e,t){function n(n){var i=p(n);e.exifdata=i||{};var s=function(e){var t=new DataView(e);r&&console.log("Got file of length "+e.byteLength);if(255!=t.getUint8(0)||216!=t.getUint8(1))return r&&console.log("Not a valid JPEG"),!1;var n=2,i=e.byteLength,a=function(e,t){return 56===e.getUint8(t)&&66===e.getUint8(t+1)&&73===e.getUint8(t+2)&&77===e.getUint8(t+3)&&4===e.getUint8(t+4)&&4===e.getUint8(t+5)};for(;n")+8,l=(c=c.substring(c.indexOf("4?p:t+8,s=[],c=0;c4?p:t+8,f-1);case 3:if(1==f)return e.getUint16(t+8,!i);for(a=f>2?p:t+8,s=[],c=0;ce.byteLength)return{};var a=g(e,t,t+i,u,n);if(a.Compression)switch(a.Compression){case 6:if(a.JpegIFOffset&&a.JpegIFByteCount){var s=t+a.JpegIFOffset,o=a.JpegIFByteCount;a.blob=new Blob([new Uint8Array(e.buffer,s,o)],{type:"image/jpeg"})}break;case 1:console.log("Thumbnail image format is TIFF, which is not implemented.");break;default:console.log("Unknown thumbnail image format '%s'",a.Compression)}else 2==a.PhotometricInterpretation&&console.log("Thumbnail image format is RGB, which is not implemented.");return a}(e,p,h,n),i}function T(e){var t={};if(1==e.nodeType){if(e.attributes.length>0){t["@attributes"]={};for(var r=0;r0)for(var r=0;r{if(!t.isBuffer(e))return{};let i={},a=e.indexOf(n);for(;-1!==a;){let t=(a=e.indexOf(n,a+n.byteLength))+n.byteLength,s=t+1,o=s+2,c=e.readUInt8(t),u=e.readUInt16BE(s);if(!r.has(c))continue;if(u>e.length-(o+u))throw new Error("Invalid IPTC directory");let l=r.get(c),d=e.slice(o,o+u).toString();null==i[l]?i[l]=d:Array.isArray(i[l])?i[l].push(d):i[l]=[i[l],d]}return i}}).call(this,r(3).Buffer)},,,,,,,,,function(e,t,r){e.exports=r(186)},function(e,t,r){"use strict";r.r(t),function(e){var t=r(174),n=r(175),i=r.n(n),a=r(176),s=r.n(a);window.mediaCloudReaderCache={readers:[],getFreeReader:function(){return this.readers.length>0?(console.log("Got cached reader."),this.readers.splice(0,1)[0]):new FileReader},cacheReader:function(e){this.readers.push(e)}},window.DirectUploadItem=Backbone.Model.extend({initialize:function(e){this.on("change:progress",this.updateProgress,this)},updateProgress:function(){var e=this.get("attachment"),t=this.get("progress");e.set({percent:t})},startUpload:function(){if("uploadToStorage"in DirectUploadItem.prototype){var e=this.get("file"),t=this.get("faces"),r=this.get("mimeType");this.get("isDirectUpload")?(console.log("Uploading to Cloud Storage"),this.uploadToStorage(e,t,r,this)):this.uploadToWordPress(e,t,this)}else alert("Direct upload is misconfigured.")},uploadToWordPress:function(e,t,r){console.log("Uploading to WordPress");var n=new FormData;n.append("action",_wpPluploadSettings.defaults.multipart_params.action),n.append("_wpnonce",_wpPluploadSettings.defaults.multipart_params._wpnonce),n.append("name",e.name),n.append("async-upload",e),jQuery.ajax({url:_wpPluploadSettings.defaults.url,method:"POST",contentType:!1,processData:!1,data:n,dataType:"json",xhr:function(){var e=jQuery.ajaxSettings.xhr();return e.upload.onprogress=function(e){r.set({progress:e.loaded/e.total*100})}.bind(this),e},success:function(e){e.hasOwnProperty("success")&&e.success?r.wordpressUploadFinished(e,t):r.uploadError()},error:function(e){r.uploadError()}})},finishWordpressUpload:function(e){var t=this.get("attachment");_.each(["file","loaded","size","percent"],function(e){t.unset(e)}),t.set(_.extend(e.data,{uploading:!1})),wp.media.model.Attachment.get(e.data.id,t),this.set({state:"done"}),wp.Uploader.queue.remove(t),wp.Uploader.queue.all(function(e){return!e.get("uploading")})&&wp.Uploader.queue.reset()},wordpressUploadFinished:function(e,t){if(null!=t){var r={action:"ilab_add_face_data",post_id:e.data.id,faces:t};jQuery.post(ajaxurl,r,function(t){this.finishWordpressUpload(e)}.bind(this))}else this.finishWordpressUpload(e)},doUploadFinished:function(e,t,r){var n=this.get("attachment"),i={action:"ilab_upload_import_cloud_file",key:e};null!=r&&(i.metadata=r),console.log("Upload finished"),console.log(i),null!=t&&t.length>0&&(i.faces=t),jQuery.post(ajaxurl,i,function(e){_.each(["file","loaded","size","percent"],function(e){n.unset(e)}),"success"==e.status?(n.set(_.extend(e.attachment,{uploading:!1})),wp.media.model.Attachment.get(e.attachment.id,n),this.set({state:"done"})):this.set({state:"error"}),wp.Uploader.queue.remove(n),wp.Uploader.queue.all(function(e){return!e.get("uploading")})&&wp.Uploader.queue.reset()}.bind(this))},mapID3Meta:function(e,t,r,n){r.hasOwnProperty(e)&&(n[t]=r[e])},mapIndexedID3Meta:function(e,t,r,n){r.hasOwnProperty(e)&&r[e].length>0&&(n[t]=r[e][0])},assignAudioMetadata:function(e,t){if(this.mapID3Meta("album","album",e.common,t),this.mapID3Meta("artist","artist",e.common,t),this.mapIndexedID3Meta("genre","genre",e.common,t),this.mapID3Meta("title","title",e.common,t),e.common.hasOwnProperty("track")&&null!=e.common.track.no&&(t.track_number=e.common.track.no),this.mapID3Meta("year","year",e.common,t),this.mapIndexedID3Meta("composer","composer",e.common,t),this.mapIndexedID3Meta("lyricist","lyricist",e.common,t),this.mapIndexedID3Meta("writer","writer",e.common,t),this.mapIndexedID3Meta("conductor","conductor",e.common,t),this.mapIndexedID3Meta("remixer","remixer",e.common,t),this.mapIndexedID3Meta("arranger","arranger",e.common,t),this.mapIndexedID3Meta("engineer","engineer",e.common,t),this.mapIndexedID3Meta("producer","producer",e.common,t),this.mapIndexedID3Meta("djmixer","dj_mixer",e.common,t),this.mapIndexedID3Meta("mixer","mixer",e.common,t),this.mapIndexedID3Meta("technician","technician",e.common,t),this.mapIndexedID3Meta("label","label",e.common,t),this.mapIndexedID3Meta("subtitle","subtitle",e.common,t),this.mapIndexedID3Meta("compilation","compilation",e.common,t),this.mapID3Meta("bpm","bpm",e.common,t),this.mapID3Meta("mood","mood",e.common,t),this.mapID3Meta("media","media",e.common,t),this.mapID3Meta("tvShow","tv_show",e.common,t),this.mapID3Meta("tvSeason","tv_season",e.common,t),this.mapID3Meta("tvEpisode","tv_episode",e.common,t),this.mapID3Meta("tvNetwork","tv_network",e.common,t),this.mapID3Meta("podcast","podcast",e.common,t),this.mapID3Meta("podcasturl","podcast_url",e.common,t),this.mapID3Meta("releasestatus","release_status",e.common,t),this.mapID3Meta("releasetype","release_type",e.common,t),this.mapID3Meta("releasecountry","release_country",e.common,t),this.mapID3Meta("language","language",e.common,t),this.mapID3Meta("copyright","copyright",e.common,t),this.mapID3Meta("license","license",e.common,t),this.mapID3Meta("encodedby","encoded_by",e.common,t),this.mapID3Meta("encodersettings","encoder_options",e.common,t),this.mapID3Meta("gapless","gapless",e.common,t),this.mapID3Meta("barcode","barcode",e.common,t),this.mapID3Meta("asin","asin",e.common,t),this.mapID3Meta("website","website",e.common,t),this.mapID3Meta("averageLevel","average_level",e.common,t),this.mapID3Meta("peakLevel","peak_level",e.common,t),this.mapID3Meta("bitrate","bitrate",e.format,t),this.mapID3Meta("codec","dataformat",e.format,t),this.mapID3Meta("codecProfile","bitrate_mode",e.format,t),this.mapID3Meta("lossless","lossless",e.format,t),this.mapID3Meta("numberOfChannels","channels",e.format,t),this.mapID3Meta("duration","length",e.format,t),this.mapID3Meta("sampleRate","sample_rate",e.format,t),e.common.hasOwnProperty("picture")&&e.common.picture.length>0){var r=e.common.picture[0];t.thumbnail={mimeType:r.format,data:r.data.toString("base64")},t.image=[{mime:r.format}]}},mapImageMeta:function(e,t,r,n,i){if(t.hasOwnProperty(r)){var a=t[r];if("string"==e){if(!(o="string"==typeof a||a instanceof String))return;n[i]=a.trim()}else if("strings"==e){if(o="string"==typeof a||a instanceof String){a=a.split(",");for(var s=0;s0&&(n[i]=a)}else if("date"==e){var o;if(!(o="string"==typeof a||a instanceof String))return;var c=a.split(" ");if(2!=c.length)return;var u=c[0].split(":");if(3!=u.length)return;var l=u[0],d=u[1],f=u[2],p=new Date("".concat(d,"/").concat(f,"/").concat(l," ").concat(c[1]));n[i]=(p.getTime()/1e3).toFixed(0)}else if("frac"==e){if(o)return;if(!("number"==typeof a||a instanceof Number))return;n[i]=a.numerator/a.denominator}else if("number"==e){if(o)return;if(!("number"==typeof a||a instanceof Number))return;n[i]=a}}},assignImageMetadata:function(e,t,r){console.log(e),console.log(t);var n={};this.mapImageMeta("string",e,"headline",n,"title"),this.mapImageMeta("string",e,"caption",n,"caption"),this.mapImageMeta("string",e,"credit",n,"credit"),this.mapImageMeta("string",e,"copyright",n,"copyright"),this.mapImageMeta("strings",e,"keywords",n,"keywords"),0!=t&&(this.mapImageMeta("string",t,"ImageDescription",n,"title"),n.hasOwnProperty("caption")||this.mapImageMeta("bytes",t,"UserComment",n,"caption"),n.hasOwnProperty("copyright")||this.mapImageMeta("bytes",t,"Copyright",n,"copyright"),this.mapImageMeta("string",t,"Artist",n,"credit"),n.hasOwnProperty("credit")||this.mapImageMeta("string",t,"Author",n,"credit"),this.mapImageMeta("string",t,"Copyright",n,"copyright"),this.mapImageMeta("string",t,"Model",n,"camera"),this.mapImageMeta("string",t,"ISOSpeedRatings",n,"iso"),this.mapImageMeta("number",t,"ISOSpeedRatings",n,"iso"),this.mapImageMeta("date",t,"DateTimeDigitized",n,"created_timestamp"),this.mapImageMeta("frac",t,"FocalLength",n,"focal_length"),this.mapImageMeta("frac",t,"FNumber",n,"aperture"),this.mapImageMeta("frac",t,"ExposureTime",n,"shutter_speed"),this.mapImageMeta("number",t,"Orientation",n,"orientation")),r.image_meta=n},uploadFinished:function(r,n){var a=this.get("attachment").get("file"),o={filesize:a.size},c=this.get("mimeType");if(0===c.indexOf("audio/")){var u=c.split("/")[1];o.fileformat=u,t.parseBlob(a,{duration:!0}).then(function(e){this.assignAudioMetadata(e,o),this.doUploadFinished(r,n,o)}.bind(this)).catch(function(e){this.doUploadFinished(r,n,o)}.bind(this))}else if(0==c.indexOf("image/")){var l=mediaCloudReaderCache.getFreeReader();l.onload=function(t){var a={};try{a=s()(e.from(l.result))}catch(e){console.log("IPTC Error",e)}var c=i.a.readFromBinaryFile(l.result);this.assignImageMetadata(a,c,o),mediaCloudReaderCache.cacheReader(l),this.doUploadFinished(r,n,o)}.bind(this),l.onerror=function(e){mediaCloudReaderCache.cacheReader(l),this.doUploadFinished(r,n,o)}.bind(this),l.readAsArrayBuffer(a)}else this.doUploadFinished(r,n,o)},uploadError:function(){this.set({state:"error"});var e=this.get("attachment");_.each(["file","loaded","size","percent"],function(t){e.unset(t)}),wp.Uploader.queue.remove(e),e.destroy()}}),window.DirectUploader=Backbone.Model.extend({initialize:function(e){this.totalFilesDropped=0,this.totalFilesUploaded=0,this.files=[],this.toBulkProcess=[],this.uploadingFiles=[],this.watchToken=0,this.settings=mediaCloudDirectUploadSettings},queueNext:function(){if(clearTimeout(this.watchToken),this.uploadingFiles.length0){for(var e=this.settings.maxUploads-this.uploadingFiles.length,t=0;t0){var r=this.files.shift();this.uploadingFiles.push(r),r.startUpload()}}else if(0==this.uploadingFiles.length&&this.toBulkProcess.length>0&&this.totalFilesDropped==this.totalFilesUploaded){var n={action:"ilab_upload_process_batch",batch:this.toBulkProcess};this.toBulkProcess=[],this.totalFilesDropped=0,this.totalFilesUploaded=0,jQuery.post(ajaxurl,n,function(e){})}this.watchToken=setTimeout(this.queueNext.bind(this),500)},addFile:function(e){if(console.log("Adding File"),""==e.type)return!1;var t=e.type;"application/x-photoshop"==t&&(t="image/psd");var r=-1!=this.settings.allowedMimes.indexOf(t);console.log("Is Direct Upload?",r);var n=t.split("/"),i=n[0],a=n[1];a="jpg"==a?"jpeg":a,this.totalFilesDropped++;var s={file:e,uploading:!0,date:new Date,filename:e.name,isDirectUpload:r,menuOrder:0,uploadedTo:wp.media.model.settings.post.id,loaded:0,size:e.size,percent:0,type:i,subType:a},o=wp.media.model.Attachment.create(s);wp.Uploader.queue.add(o);var c=new DirectUploadItem({attachment:o,file:e,mimeType:t,isDirectUpload:r,progress:0,driver:this.settings.driver,faces:null,state:"waiting"});c.on("change:state",function(e,t){if("done"==t||"error"==t){this.totalFilesUploaded++,"done"==t&&this.toBulkProcess.push(o.id);var r=this.files.indexOf(c);r>-1&&this.files.splice(r),(r=this.uploadingFiles.indexOf(c))>-1&&this.uploadingFiles.splice(r),this.queueNext()}}.bind(this)),"undefined"!=typeof ILABFaceDetector?ILABFaceDetector(e,function(e){c.set("faces",e),this.files.push(c)}.bind(this)):this.files.push(c)}}),wp.media.view.UploaderWindow.prototype.__ready=wp.media.view.UploaderWindow.prototype.ready,wp.media.view.UploaderWindow.prototype.ready=function(){this.__ready(),this.directUploader=new DirectUploader({}),this.uploader.uploader.unbind("FilesAdded"),this.uploader.uploader.bind("FilesAdded",function(e,t){_.each(t,function(e){this.directUploader.addFile(e.getNative())}.bind(this)),this.directUploader.queueNext()}.bind(this))},wp.media.view.UploaderInline=wp.media.view.UploaderInline.extend({template:wp.template("media-cloud-direct-upload")})}.call(this,r(3).Buffer)},function(e,t,r){"use strict";t.byteLength=function(e){var t=u(e),r=t[0],n=t[1];return 3*(r+n)/4-n},t.toByteArray=function(e){for(var t,r=u(e),n=r[0],s=r[1],o=new a(function(e,t,r){return 3*(t+r)/4-r}(0,n,s)),c=0,l=s>0?n-4:n,d=0;d>16&255,o[c++]=t>>8&255,o[c++]=255&t;2===s&&(t=i[e.charCodeAt(d)]<<2|i[e.charCodeAt(d+1)]>>4,o[c++]=255&t);1===s&&(t=i[e.charCodeAt(d)]<<10|i[e.charCodeAt(d+1)]<<4|i[e.charCodeAt(d+2)]>>2,o[c++]=t>>8&255,o[c++]=255&t);return o},t.fromByteArray=function(e){for(var t,r=e.length,i=r%3,a=[],s=0,o=r-i;so?o:s+16383));1===i?(t=e[r-1],a.push(n[t>>2]+n[t<<4&63]+"==")):2===i&&(t=(e[r-2]<<8)+e[r-1],a.push(n[t>>10]+n[t>>4&63]+n[t<<2&63]+"="));return a.join("")};for(var n=[],i=[],a="undefined"!=typeof Uint8Array?Uint8Array:Array,s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",o=0,c=s.length;o0)throw new Error("Invalid string. Length must be a multiple of 4");var r=e.indexOf("=");return-1===r&&(r=t),[r,r===t?0:4-r%4]}function l(e,t,r){for(var i,a,s=[],o=t;o>18&63]+n[a>>12&63]+n[a>>6&63]+n[63&a]);return s.join("")}i["-".charCodeAt(0)]=62,i["_".charCodeAt(0)]=63},function(e,t){t.read=function(e,t,r,n,i){var a,s,o=8*i-n-1,c=(1<>1,l=-7,d=r?i-1:0,f=r?-1:1,p=e[t+d];for(d+=f,a=p&(1<<-l)-1,p>>=-l,l+=o;l>0;a=256*a+e[t+d],d+=f,l-=8);for(s=a&(1<<-l)-1,a>>=-l,l+=n;l>0;s=256*s+e[t+d],d+=f,l-=8);if(0===a)a=1-u;else{if(a===c)return s?NaN:1/0*(p?-1:1);s+=Math.pow(2,n),a-=u}return(p?-1:1)*s*Math.pow(2,a-n)},t.write=function(e,t,r,n,i,a){var s,o,c,u=8*a-i-1,l=(1<>1,f=23===i?Math.pow(2,-24)-Math.pow(2,-77):0,p=n?0:a-1,h=n?1:-1,m=t<0||0===t&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(o=isNaN(t)?1:0,s=l):(s=Math.floor(Math.log(t)/Math.LN2),t*(c=Math.pow(2,-s))<1&&(s--,c*=2),(t+=s+d>=1?f/c:f*Math.pow(2,1-d))*c>=2&&(s++,c/=2),s+d>=l?(o=0,s=l):s+d>=1?(o=(t*c-1)*Math.pow(2,i),s+=d):(o=t*Math.pow(2,d-1)*Math.pow(2,i),s=0));i>=8;e[r+p]=255&o,p+=h,o/=256,i-=8);for(s=s<0;e[r+p]=255&s,p+=h,s/=256,u-=8);e[r+p-h]|=128*m}},function(e,t,r){(function(n){t.log=function(...e){return"object"==typeof console&&console.log&&console.log(...e)},t.formatArgs=function(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+e.exports.humanize(this.diff),!this.useColors)return;const r="color: "+this.color;t.splice(1,0,r,"color: inherit");let n=0,i=0;t[0].replace(/%[a-zA-Z%]/g,e=>{"%%"!==e&&(n++,"%c"===e&&(i=n))}),t.splice(i,0,r)},t.save=function(e){try{e?t.storage.setItem("debug",e):t.storage.removeItem("debug")}catch(e){}},t.load=function(){let e;try{e=t.storage.getItem("debug")}catch(e){}!e&&void 0!==n&&"env"in n&&(e=n.env.DEBUG);return e},t.useColors=function(){if("undefined"!=typeof window&&window.process&&("renderer"===window.process.type||window.process.__nwjs))return!0;if("undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))return!1;return"undefined"!=typeof document&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||"undefined"!=typeof window&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)},t.storage=function(){try{return localStorage}catch(e){}}(),t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"],e.exports=r(190)(t);const{formatters:i}=e.exports;i.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}}).call(this,r(9))},function(e,t,r){e.exports=function(e){function t(e){let t=0;for(let r=0;r{if("%%"===r)return r;o++;const a=n.formatters[i];if("function"==typeof a){const n=e[o];r=a.call(t,n),e.splice(o,1),o--}return r}),n.formatArgs.call(t,e),(t.log||n.log).apply(t,e)}return s.namespace=e,s.enabled=n.enabled(e),s.useColors=n.useColors(),s.color=t(e),s.destroy=i,s.extend=a,"function"==typeof n.init&&n.init(s),n.instances.push(s),s}function i(){const e=n.instances.indexOf(this);return-1!==e&&(n.instances.splice(e,1),!0)}function a(e,t){const r=n(this.namespace+(void 0===t?":":t)+e);return r.log=this.log,r}function s(e){return e.toString().substring(2,e.toString().length-2).replace(/\.\*\?$/,"*")}return n.debug=n,n.default=n,n.coerce=function(e){return e instanceof Error?e.stack||e.message:e},n.disable=function(){const e=[...n.names.map(s),...n.skips.map(s).map(e=>"-"+e)].join(",");return n.enable(""),e},n.enable=function(e){let t;n.save(e),n.names=[],n.skips=[];const r=("string"==typeof e?e:"").split(/[\s,]+/),i=r.length;for(t=0;t{n[t]=e[t]}),n.instances=[],n.names=[],n.skips=[],n.formatters={},n.selectColor=t,n.enable(n.load()),n}},function(e,t){var r=1e3,n=60*r,i=60*n,a=24*i,s=7*a,o=365.25*a;function c(e,t,r,n){var i=t>=1.5*r;return Math.round(e/r)+" "+n+(i?"s":"")}e.exports=function(e,t){t=t||{};var u=typeof e;if("string"===u&&e.length>0)return function(e){if((e=String(e)).length>100)return;var t=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(!t)return;var c=parseFloat(t[1]);switch((t[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return c*o;case"weeks":case"week":case"w":return c*s;case"days":case"day":case"d":return c*a;case"hours":case"hour":case"hrs":case"hr":case"h":return c*i;case"minutes":case"minute":case"mins":case"min":case"m":return c*n;case"seconds":case"second":case"secs":case"sec":case"s":return c*r;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return c;default:return}}(e);if("number"===u&&isFinite(e))return t.long?function(e){var t=Math.abs(e);if(t>=a)return c(e,t,a,"day");if(t>=i)return c(e,t,i,"hour");if(t>=n)return c(e,t,n,"minute");if(t>=r)return c(e,t,r,"second");return e+" ms"}(e):function(e){var t=Math.abs(e);if(t>=a)return Math.round(e/a)+"d";if(t>=i)return Math.round(e/i)+"h";if(t>=n)return Math.round(e/n)+"m";if(t>=r)return Math.round(e/r)+"s";return e+"ms"}(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(21),i=r(204);function a(e,t,r){return!e.fileSize&&r.fileSize&&(e.fileSize=r.fileSize),i.ParserFactory.parse(e,t,r)}t.parseStream=function(e,t,r={}){return a(n.fromStream(e),t,r)},t.parseBuffer=function(e,t,r={}){return a(n.fromBuffer(e),t,r)},t.parseFromTokenizer=a,t.orderTags=function(e){const t={};for(const r of e)(t[r.id]=t[r.id]||[]).push(r.value);return t},t.ratingToStars=function(e){return void 0===e?0:1+Math.round(4*e)}},function(e,t,r){"use strict";(function(e){Object.defineProperty(t,"__esModule",{value:!0});const n=r(194),i=r(10),a=r(195),s=r(200)("strtok3:ReadStreamTokenizer");t.ReadStreamTokenizer=class extends n.AbstractTokenizer{constructor(e,t){super(),this.streamReader=new a.StreamReader(e),this.fileSize=t}async readBuffer(e,t=0,r=e.length,n){if(0===r)return 0;if(n){const i=n-this.position;if(i>0)return await this.ignore(n-this.position),this.readBuffer(e,t,r);if(i<0)throw new Error("Cannot read from a negative offset in a stream")}let s;try{s=await this.streamReader.read(e,t,r),this.position+=s}catch(e){throw e.message===a.endOfStream?new Error(i.endOfFile):e}if(s0){const a=e.alloc(n+i);return c=await this.peekBuffer(a,0,i+n,void 0,o),a.copy(t,r,i),c-i}if(i<0)throw new Error("Cannot peek from a negative offset in a stream")}try{c=await this.streamReader.peek(t,r,n)}catch(e){throw e.message===a.endOfStream?new Error(i.endOfFile):e}if(!o&&c{this.reject=t,this.resolve=e})}}t.endOfStream="End-Of-Stream";t.StreamReader=class{constructor(e){if(this.s=e,this.endOfStream=!1,this.peekQueue=[],!e.read||!e.once)throw new Error("Expected an instance of stream.Readable");this.s.once("end",()=>this.reject(new Error(t.endOfStream))),this.s.once("error",e=>this.reject(e)),this.s.once("close",()=>this.reject(new Error("Stream closed")))}async peek(e,t,r){const n=await this.read(e,t,r);return this.peekQueue.push(e.slice(t,t+n)),n}async read(e,r,n){if(0===n)return 0;if(0===this.peekQueue.length&&this.endOfStream)throw new Error(t.endOfStream);let i=n,a=0;for(;this.peekQueue.length>0&&i>0;){const t=this.peekQueue.pop(),n=Math.min(t.length,i);t.copy(e,r+a,0,n),a+=n,i-=n,n0&&!this.endOfStream&&(a+=await this._read(e,r+a,i)),a}async _read(e,t,r){n.ok(!this.request,"Concurrent read operation?");const a=this.s.read(r);return a?(a.copy(e,t),a.length):(this.request={buffer:e,offset:t,length:r,deferred:new i},this.s.once("readable",()=>{this.tryRead()}),this.request.deferred.promise.then(e=>(this.request=null,e)).catch(e=>{throw this.request=null,e}))}tryRead(){const e=this.s.read(this.request.length);e?(e.copy(this.request.buffer,this.request.offset),this.request.deferred.resolve(e.length)):this.s.once("readable",()=>{this.tryRead()})}reject(e){this.endOfStream=!0,this.request&&(this.request.deferred.reject(e),this.request=null)}}},function(e,t,r){"use strict";var n=Object.getOwnPropertySymbols,i=Object.prototype.hasOwnProperty,a=Object.prototype.propertyIsEnumerable;function s(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},r=0;r<10;r++)t["_"+String.fromCharCode(r)]=r;if("0123456789"!==Object.getOwnPropertyNames(t).map(function(e){return t[e]}).join(""))return!1;var n={};return"abcdefghijklmnopqrst".split("").forEach(function(e){n[e]=e}),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},n)).join("")}catch(e){return!1}}()?Object.assign:function(e,t){for(var r,o,c=s(e),u=1;u=a)return e;switch(e){case"%s":return String(n[r++]);case"%d":return Number(n[r++]);case"%j":try{return JSON.stringify(n[r++])}catch(e){return"[Circular]"}default:return e}}),c=n[r];r=3&&(n.depth=arguments[2]),arguments.length>=4&&(n.colors=arguments[3]),h(r)?n.showHidden=r:r&&t._extend(n,r),y(n.showHidden)&&(n.showHidden=!1),y(n.depth)&&(n.depth=2),y(n.colors)&&(n.colors=!1),y(n.customInspect)&&(n.customInspect=!0),n.colors&&(n.stylize=c),l(n,e,n.depth)}function c(e,t){var r=o.styles[t];return r?"["+o.colors[r][0]+"m"+e+"["+o.colors[r][1]+"m":e}function u(e,t){return e}function l(e,r,n){if(e.customInspect&&r&&k(r.inspect)&&r.inspect!==t.inspect&&(!r.constructor||r.constructor.prototype!==r)){var i=r.inspect(n,e);return b(i)||(i=l(e,i,n)),i}var a=function(e,t){if(y(t))return e.stylize("undefined","undefined");if(b(t)){var r="'"+JSON.stringify(t).replace(/^"|"$/g,"").replace(/'/g,"\\'").replace(/\\"/g,'"')+"'";return e.stylize(r,"string")}if(g(t))return e.stylize(""+t,"number");if(h(t))return e.stylize(""+t,"boolean");if(m(t))return e.stylize("null","null")}(e,r);if(a)return a;var s=Object.keys(r),o=function(e){var t={};return e.forEach(function(e,r){t[e]=!0}),t}(s);if(e.showHidden&&(s=Object.getOwnPropertyNames(r)),I(r)&&(s.indexOf("message")>=0||s.indexOf("description")>=0))return d(r);if(0===s.length){if(k(r)){var c=r.name?": "+r.name:"";return e.stylize("[Function"+c+"]","special")}if(w(r))return e.stylize(RegExp.prototype.toString.call(r),"regexp");if(v(r))return e.stylize(Date.prototype.toString.call(r),"date");if(I(r))return d(r)}var u,T="",S=!1,C=["{","}"];(p(r)&&(S=!0,C=["[","]"]),k(r))&&(T=" [Function"+(r.name?": "+r.name:"")+"]");return w(r)&&(T=" "+RegExp.prototype.toString.call(r)),v(r)&&(T=" "+Date.prototype.toUTCString.call(r)),I(r)&&(T=" "+d(r)),0!==s.length||S&&0!=r.length?n<0?w(r)?e.stylize(RegExp.prototype.toString.call(r),"regexp"):e.stylize("[Object]","special"):(e.seen.push(r),u=S?function(e,t,r,n,i){for(var a=[],s=0,o=t.length;s=0&&0,e+t.replace(/\u001b\[\d\d?m/g,"").length+1},0)>60)return r[0]+(""===t?"":t+"\n ")+" "+e.join(",\n ")+" "+r[1];return r[0]+t+" "+e.join(", ")+" "+r[1]}(u,T,C)):C[0]+T+C[1]}function d(e){return"["+Error.prototype.toString.call(e)+"]"}function f(e,t,r,n,i,a){var s,o,c;if((c=Object.getOwnPropertyDescriptor(t,i)||{value:t[i]}).get?o=c.set?e.stylize("[Getter/Setter]","special"):e.stylize("[Getter]","special"):c.set&&(o=e.stylize("[Setter]","special")),F(n,i)||(s="["+i+"]"),o||(e.seen.indexOf(c.value)<0?(o=m(r)?l(e,c.value,null):l(e,c.value,r-1)).indexOf("\n")>-1&&(o=a?o.split("\n").map(function(e){return" "+e}).join("\n").substr(2):"\n"+o.split("\n").map(function(e){return" "+e}).join("\n")):o=e.stylize("[Circular]","special")),y(s)){if(a&&i.match(/^\d+$/))return o;(s=JSON.stringify(""+i)).match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)?(s=s.substr(1,s.length-2),s=e.stylize(s,"name")):(s=s.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'"),s=e.stylize(s,"string"))}return s+": "+o}function p(e){return Array.isArray(e)}function h(e){return"boolean"==typeof e}function m(e){return null===e}function g(e){return"number"==typeof e}function b(e){return"string"==typeof e}function y(e){return void 0===e}function w(e){return T(e)&&"[object RegExp]"===S(e)}function T(e){return"object"==typeof e&&null!==e}function v(e){return T(e)&&"[object Date]"===S(e)}function I(e){return T(e)&&("[object Error]"===S(e)||e instanceof Error)}function k(e){return"function"==typeof e}function S(e){return Object.prototype.toString.call(e)}function C(e){return e<10?"0"+e.toString(10):e.toString(10)}t.debuglog=function(r){if(y(a)&&(a=e.env.NODE_DEBUG||""),r=r.toUpperCase(),!s[r])if(new RegExp("\\b"+r+"\\b","i").test(a)){var n=e.pid;s[r]=function(){var e=t.format.apply(t,arguments);console.error("%s %d: %s",r,n,e)}}else s[r]=function(){};return s[r]},t.inspect=o,o.colors={bold:[1,22],italic:[3,23],underline:[4,24],inverse:[7,27],white:[37,39],grey:[90,39],black:[30,39],blue:[34,39],cyan:[36,39],green:[32,39],magenta:[35,39],red:[31,39],yellow:[33,39]},o.styles={special:"cyan",number:"yellow",boolean:"yellow",undefined:"grey",null:"bold",string:"green",date:"magenta",regexp:"red"},t.isArray=p,t.isBoolean=h,t.isNull=m,t.isNullOrUndefined=function(e){return null==e},t.isNumber=g,t.isString=b,t.isSymbol=function(e){return"symbol"==typeof e},t.isUndefined=y,t.isRegExp=w,t.isObject=T,t.isDate=v,t.isError=I,t.isFunction=k,t.isPrimitive=function(e){return null===e||"boolean"==typeof e||"number"==typeof e||"string"==typeof e||"symbol"==typeof e||void 0===e},t.isBuffer=r(198);var E=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function _(){var e=new Date,t=[C(e.getHours()),C(e.getMinutes()),C(e.getSeconds())].join(":");return[e.getDate(),E[e.getMonth()],t].join(" ")}function F(e,t){return Object.prototype.hasOwnProperty.call(e,t)}t.log=function(){console.log("%s - %s",_(),t.format.apply(t,arguments))},t.inherits=r(199),t._extend=function(e,t){if(!t||!T(t))return e;for(var r=Object.keys(t),n=r.length;n--;)e[r[n]]=t[r[n]];return e};var A="undefined"!=typeof Symbol?Symbol("util.promisify.custom"):void 0;function x(e,t){if(!e){var r=new Error("Promise was rejected with a falsy value");r.reason=e,e=r}return t(e)}t.promisify=function(e){if("function"!=typeof e)throw new TypeError('The "original" argument must be of type Function');if(A&&e[A]){var t;if("function"!=typeof(t=e[A]))throw new TypeError('The "util.promisify.custom" argument must be of type Function');return Object.defineProperty(t,A,{value:t,enumerable:!1,writable:!1,configurable:!0}),t}function t(){for(var t,r,n=new Promise(function(e,n){t=e,r=n}),i=[],a=0;a{"%%"!==e&&(n++,"%c"===e&&(i=n))}),t.splice(i,0,r)},t.save=function(e){try{e?t.storage.setItem("debug",e):t.storage.removeItem("debug")}catch(e){}},t.load=function(){let e;try{e=t.storage.getItem("debug")}catch(e){}!e&&void 0!==n&&"env"in n&&(e=n.env.DEBUG);return e},t.useColors=function(){if("undefined"!=typeof window&&window.process&&("renderer"===window.process.type||window.process.__nwjs))return!0;if("undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))return!1;return"undefined"!=typeof document&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||"undefined"!=typeof window&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)},t.storage=function(){try{return localStorage}catch(e){}}(),t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"],e.exports=r(201)(t);const{formatters:i}=e.exports;i.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}}).call(this,r(9))},function(e,t,r){e.exports=function(e){function t(e){let t=0;for(let r=0;r{if("%%"===r)return r;o++;const a=n.formatters[i];if("function"==typeof a){const n=e[o];r=a.call(t,n),e.splice(o,1),o--}return r}),n.formatArgs.call(t,e),(t.log||n.log).apply(t,e)}return s.namespace=e,s.enabled=n.enabled(e),s.useColors=n.useColors(),s.color=t(e),s.destroy=i,s.extend=a,"function"==typeof n.init&&n.init(s),n.instances.push(s),s}function i(){const e=n.instances.indexOf(this);return-1!==e&&(n.instances.splice(e,1),!0)}function a(e,t){const r=n(this.namespace+(void 0===t?":":t)+e);return r.log=this.log,r}function s(e){return e.toString().substring(2,e.toString().length-2).replace(/\.\*\?$/,"*")}return n.debug=n,n.default=n,n.coerce=function(e){return e instanceof Error?e.stack||e.message:e},n.disable=function(){const e=[...n.names.map(s),...n.skips.map(s).map(e=>"-"+e)].join(",");return n.enable(""),e},n.enable=function(e){let t;n.save(e),n.names=[],n.skips=[];const r=("string"==typeof e?e:"").split(/[\s,]+/),i=r.length;for(t=0;t{n[t]=e[t]}),n.instances=[],n.names=[],n.skips=[],n.formatters={},n.selectColor=t,n.enable(n.load()),n}},function(e,t){var r=1e3,n=60*r,i=60*n,a=24*i,s=7*a,o=365.25*a;function c(e,t,r,n){var i=t>=1.5*r;return Math.round(e/r)+" "+n+(i?"s":"")}e.exports=function(e,t){t=t||{};var u=typeof e;if("string"===u&&e.length>0)return function(e){if((e=String(e)).length>100)return;var t=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(!t)return;var c=parseFloat(t[1]);switch((t[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return c*o;case"weeks":case"week":case"w":return c*s;case"days":case"day":case"d":return c*a;case"hours":case"hour":case"hrs":case"hr":case"h":return c*i;case"minutes":case"minute":case"mins":case"min":case"m":return c*n;case"seconds":case"second":case"secs":case"sec":case"s":return c*r;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return c;default:return}}(e);if("number"===u&&isFinite(e))return t.long?function(e){var t=Math.abs(e);if(t>=a)return c(e,t,a,"day");if(t>=i)return c(e,t,i,"hour");if(t>=n)return c(e,t,n,"minute");if(t>=r)return c(e,t,r,"second");return e+" ms"}(e):function(e){var t=Math.abs(e);if(t>=a)return Math.round(e/a)+"d";if(t>=i)return Math.round(e/i)+"h";if(t>=n)return Math.round(e/n)+"m";if(t>=r)return Math.round(e/r)+"s";return e+"ms"}(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(10);t.BufferTokenizer=class{constructor(e){this.buffer=e,this.position=0,this.fileSize=e.length}async readBuffer(e,t,r,n){return this.position=n||this.position,this.peekBuffer(e,t,r,this.position).then(e=>(this.position+=e,e))}async peekBuffer(e,t,r,i,a=!1){i=i||this.position,r||(r=e.length);const s=Math.min(this.buffer.length-i,r);if(!a&&s[...e].map(e=>e.charCodeAt(0));const r=(e,t,r)=>String.fromCharCode(...e.slice(t,r));t.readUInt64LE=(e,t=0)=>{let r=e[t],n=1,i=0;for(;++i<8;)n*=256,r+=e[t+i]*n;return r},t.tarHeaderChecksumMatches=e=>{if(e.length<512)return!1;let t=256,n=0;for(let r=0;r<148;r++){const i=e[r];t+=i,n+=128&i}for(let r=156;r<512;r++){const i=e[r];t+=i,n+=128&i}const i=parseInt(r(e,148,154),8);return i===t||i===t-(n<<1)},t.multiByteIndexOf=(t,r,n=0)=>{if(e&&e.isBuffer(t))return t.indexOf(e.from(r),n);const i=(e,t,r)=>{for(let n=1;n=0;){if(i(t,r,a))return a;a=t.indexOf(r[0],a+1)}return-1},t.uint8ArrayUtf8ByteString=r}).call(this,r(3).Buffer)},function(e,t,r){"use strict";e.exports={extensions:["jpg","png","apng","gif","webp","flif","cr2","orf","arw","dng","nef","rw2","raf","tif","bmp","jxr","psd","zip","tar","rar","gz","bz2","7z","dmg","mp4","mid","mkv","webm","mov","avi","wmv","mpg","mp2","mp3","m4a","oga","ogg","ogv","opus","flac","wav","spx","amr","pdf","epub","exe","swf","rtf","wasm","woff","woff2","eot","ttf","otf","ico","flv","ps","xz","sqlite","nes","crx","xpi","cab","deb","ar","rpm","Z","lz","msi","mxf","mts","blend","bpg","docx","pptx","xlsx","3gp","3g2","jp2","jpm","jpx","mj2","aif","qcp","odt","ods","odp","xml","mobi","heic","cur","ktx","ape","wv","wmv","wma","dcm","ics","glb","pcap","dsf","lnk","alias","voc","ac3","m4v","m4p","m4b","f4v","f4p","f4b","f4a"],mimeTypes:["image/jpeg","image/png","image/gif","image/webp","image/flif","image/x-canon-cr2","image/tiff","image/bmp","image/vnd.ms-photo","image/vnd.adobe.photoshop","application/epub+zip","application/x-xpinstall","application/vnd.oasis.opendocument.text","application/vnd.oasis.opendocument.spreadsheet","application/vnd.oasis.opendocument.presentation","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/vnd.openxmlformats-officedocument.presentationml.presentation","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","application/zip","application/x-tar","application/x-rar-compressed","application/gzip","application/x-bzip2","application/x-7z-compressed","application/x-apple-diskimage","video/mp4","audio/midi","video/x-matroska","video/webm","video/quicktime","video/vnd.avi","audio/vnd.wave","audio/qcelp","audio/x-ms-wma","video/x-ms-asf","application/vnd.ms-asf","video/mpeg","video/3gpp","audio/mpeg","audio/mp4","audio/opus","video/ogg","audio/ogg","application/ogg","audio/x-flac","audio/ape","audio/wavpack","audio/amr","application/pdf","application/x-msdownload","application/x-shockwave-flash","application/rtf","application/wasm","font/woff","font/woff2","application/vnd.ms-fontobject","font/ttf","font/otf","image/x-icon","video/x-flv","application/postscript","application/x-xz","application/x-sqlite3","application/x-nintendo-nes-rom","application/x-google-chrome-extension","application/vnd.ms-cab-compressed","application/x-deb","application/x-unix-archive","application/x-rpm","application/x-compress","application/x-lzip","application/x-msi","application/mxf","video/mp2t","application/x-blender","image/bpg","image/jp2","image/jpx","image/jpm","image/mj2","audio/aiff","application/xml","application/x-mobipocket-ebook","image/heif","image/heif-sequence","image/heic","image/heic-sequence","image/ktx","application/dicom","audio/x-musepack","text/calendar","model/gltf-binary","application/vnd.tcpdump.pcap","audio/x-dsf","application/x.ms.shortcut","application/x.apple.alias","audio/x-voc","audio/vnd.dolby.dd-raw","audio/x-m4a"]}},function(e,t,r){"use strict";var n=/; *([!#$%&'*+.^_`|~0-9A-Za-z-]+) *= *("(?:[\u000b\u0020\u0021\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\u000b\u0020-\u00ff])*"|[!#$%&'*+.^_`|~0-9A-Za-z-]+) */g,i=/^[\u000b\u0020-\u007e\u0080-\u00ff]+$/,a=/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/,s=/\\([\u000b\u0020-\u00ff])/g,o=/([\\"])/g,c=/^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;function u(e){var t=String(e);if(a.test(t))return t;if(t.length>0&&!i.test(t))throw new TypeError("invalid parameter value");return'"'+t.replace(o,"\\$1")+'"'}function l(e){this.parameters=Object.create(null),this.type=e}t.format=function(e){if(!e||"object"!=typeof e)throw new TypeError("argument obj is required");var t=e.parameters,r=e.type;if(!r||!c.test(r))throw new TypeError("invalid type");var n=r;if(t&&"object"==typeof t)for(var i,s=Object.keys(t).sort(),o=0;o{if("%%"===r)return r;o++;const a=n.formatters[i];if("function"==typeof a){const n=e[o];r=a.call(t,n),e.splice(o,1),o--}return r}),n.formatArgs.call(t,e),(t.log||n.log).apply(t,e)}return s.namespace=e,s.enabled=n.enabled(e),s.useColors=n.useColors(),s.color=t(e),s.destroy=i,s.extend=a,"function"==typeof n.init&&n.init(s),n.instances.push(s),s}function i(){const e=n.instances.indexOf(this);return-1!==e&&(n.instances.splice(e,1),!0)}function a(e,t){const r=n(this.namespace+(void 0===t?":":t)+e);return r.log=this.log,r}function s(e){return e.toString().substring(2,e.toString().length-2).replace(/\.\*\?$/,"*")}return n.debug=n,n.default=n,n.coerce=function(e){return e instanceof Error?e.stack||e.message:e},n.disable=function(){const e=[...n.names.map(s),...n.skips.map(s).map(e=>"-"+e)].join(",");return n.enable(""),e},n.enable=function(e){let t;n.save(e),n.names=[],n.skips=[];const r=("string"==typeof e?e:"").split(/[\s,]+/),i=r.length;for(t=0;t{n[t]=e[t]}),n.instances=[],n.names=[],n.skips=[],n.formatters={},n.selectColor=t,n.enable(n.load()),n}},function(e,t){var r=1e3,n=60*r,i=60*n,a=24*i,s=7*a,o=365.25*a;function c(e,t,r,n){var i=t>=1.5*r;return Math.round(e/r)+" "+n+(i?"s":"")}e.exports=function(e,t){t=t||{};var u=typeof e;if("string"===u&&e.length>0)return function(e){if((e=String(e)).length>100)return;var t=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(!t)return;var c=parseFloat(t[1]);switch((t[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return c*o;case"weeks":case"week":case"w":return c*s;case"days":case"day":case"d":return c*a;case"hours":case"hour":case"hrs":case"hr":case"h":return c*i;case"minutes":case"minute":case"mins":case"min":case"m":return c*n;case"seconds":case"second":case"secs":case"sec":case"s":return c*r;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return c;default:return}}(e);if("number"===u&&isFinite(e))return t.long?function(e){var t=Math.abs(e);if(t>=a)return c(e,t,a,"day");if(t>=i)return c(e,t,i,"hour");if(t>=n)return c(e,t,n,"minute");if(t>=r)return c(e,t,r,"second");return e+" ms"}(e):function(e){var t=Math.abs(e);if(t>=a)return Math.round(e/a)+"d";if(t>=i)return Math.round(e/i)+"h";if(t>=n)return Math.round(e/n)+"m";if(t>=r)return Math.round(e/r)+"s";return e+"ms"}(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(2),i=r(212),a=r(213),s=r(8),o=r(4),c=n("music-metadata:collector"),u=["APEv2","vorbis","ID3v2.4","ID3v2.3","ID3v2.2","exif","asf","iTunes","ID3v1"];function l(e){return e.length>2?e.slice(0,e.length-1).join(", ")+" & "+e[e.length-1]:e.join(" & ")}t.MetadataCollector=class{constructor(e){this.opts=e,this.format={tagTypes:[]},this.native={},this.common={track:{no:null,of:null},disk:{no:null,of:null}},this.commonOrigin={},this.originPriority={},this.tagMapper=new a.CombinedTagMapper;let t=1;for(const e of u)this.originPriority[e]=t++;this.originPriority.artificial=500,this.originPriority.id3v1=600}hasAny(){for(const e in this.native)return!0;return!1}setFormat(e,t){c(`format: ${e} = ${t}`),this.format[e]=t,this.opts.observer&&this.opts.observer({metadata:this,tag:{type:"format",id:e,value:t}})}addTag(e,t,r){c(`tag ${e}.${t} = ${r}`),this.native[e]||(this.format.tagTypes.push(e),this.native[e]=[]),this.native[e].push({id:t,value:r}),this.toCommon(e,t,r)}getNativeMetadata(){return{format:this.format,native:this.native}}postMap(e,t){switch(t.id){case"artist":if(this.commonOrigin.artist===this.originPriority[e])return this.postMap("artificial",{id:"artists",value:t.value});this.common.artists||this.setGenericTag("artificial",{id:"artists",value:t.value});break;case"artists":if(!(this.common.artist&&this.commonOrigin.artist!==this.originPriority.artificial||this.common.artists&&-1!==this.common.artists.indexOf(t.value))){const e={id:"artist",value:l((this.common.artists||[]).concat([t.value]))};this.setGenericTag("artificial",e)}break;case"genre":t.value=s.CommonTagMapper.parseGenre(t.value);break;case"picture":t.value.format=s.CommonTagMapper.fixPictureMimeType(t.value.format);break;case"totaltracks":return void(this.common.track.of=s.CommonTagMapper.toIntOrNull(t.value));case"totaldiscs":return void(this.common.disk.of=s.CommonTagMapper.toIntOrNull(t.value));case"track":case"disk":const r=this.common[t.id].of;return this.common[t.id]=s.CommonTagMapper.normalizeTrack(t.value),void(this.common[t.id].of=null!=r?r:this.common[t.id].of);case"year":case"originalyear":t.value=parseInt(t.value,10);break;case"date":const n=parseInt(t.value.substr(0,4),10);n&&!isNaN(n)&&(this.common.year=n);break;case"discogs_label_id":case"discogs_release_id":case"discogs_master_release_id":case"discogs_artist_id":case"discogs_votes":t.value="string"==typeof t.value?parseInt(t.value,10):t.value;break;case"replaygain_track_gain":case"replaygain_track_peak":t.value=o.toRatio(t.value);break;case"gapless":t.value="1"===t.value}this.setGenericTag(e,t)}toCommonMetadata(){return{format:this.format,native:this.opts.native?this.native:void 0,common:this.common}}toCommon(e,t,r){const n={id:t,value:r},i=this.tagMapper.mapTag(e,n);i&&this.postMap(e,i)}setGenericTag(e,t){c(`common.${t.id} = ${t.value}`);const r=this.commonOrigin[t.id]||1e3,n=this.originPriority[e];if(i.isSingleton(t.id)){if(!(n<=r))return c(`Ignore native tag (singleton): ${e}.${t.id} = ${t.value}`);this.common[t.id]=t.value,this.commonOrigin[t.id]=n}else if(n===r)i.isUnique(t.id)&&-1!==this.common[t.id].indexOf(t.value)?c(`Ignore duplicate value: ${e}.${t.id} = ${t.value}`):this.common[t.id].push(t.value);else{if(!(n{this.registerTagMapper(e)})}mapTag(e,t){if(this.tagMappers[e])return this.tagMappers[e].mapGenericTag(t);throw new Error("No generic tag mapper defined for tag-format: "+e)}registerTagMapper(e){for(const t of e.tagTypes)this.tagMappers[t]=e}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(8),i={title:"title",artist:"artist",album:"album",year:"year",comment:"comment",track:"track",genre:"genre"};t.ID3v1TagMapper=class extends n.CommonTagMapper{constructor(){super(["ID3v1"],i)}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});class n{static decode(e){let t="";for(const r in e)e.hasOwnProperty(r)&&(t+=n.codePointToString(n.singleByteDecoder(e[r])));return t}static inRange(e,t,r){return t<=e&&e<=r}static codePointToString(e){return e<=65535?String.fromCharCode(e):(e-=65536,String.fromCharCode(55296+(e>>10),56320+(1023&e)))}static singleByteDecoder(e){if(n.inRange(e,0,127))return e;const t=n.windows1252[e-128];if(null===t)throw Error("invaliding encoding");return t}}n.windows1252=[8364,129,8218,402,8222,8230,8224,8225,710,8240,352,8249,338,141,381,143,144,8216,8217,8220,8221,8226,8211,8212,732,8482,353,8250,339,157,382,376,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255],t.Windows1292Decoder=n},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(8),i=r(4),a={TIT2:"title",TPE1:"artist","TXXX:Artists":"artists",TPE2:"albumartist",TALB:"album",TDRV:"date",TORY:"originalyear",TPOS:"disk",TCON:"genre",APIC:"picture",TCOM:"composer","USLT:description":"lyrics",TSOA:"albumsort",TSOT:"titlesort",TOAL:"originalalbum",TSOP:"artistsort",TSO2:"albumartistsort",TSOC:"composersort",TEXT:"lyricist","TXXX:Writer":"writer",TPE3:"conductor",TPE4:"remixer","IPLS:arranger":"arranger","IPLS:engineer":"engineer","IPLS:producer":"producer","IPLS:DJ-mix":"djmixer","IPLS:mix":"mixer",TPUB:"label",TIT1:"grouping",TIT3:"subtitle",TRCK:"track",TCMP:"compilation",POPM:"rating",TBPM:"bpm",TMED:"media","TXXX:CATALOGNUMBER":"catalognumber","TXXX:MusicBrainz Album Status":"releasestatus","TXXX:MusicBrainz Album Type":"releasetype","TXXX:MusicBrainz Album Release Country":"releasecountry","TXXX:RELEASECOUNTRY":"releasecountry","TXXX:SCRIPT":"script",TLAN:"language",TCOP:"copyright",WCOP:"license",TENC:"encodedby",TSSE:"encodersettings","TXXX:BARCODE":"barcode",TSRC:"isrc","TXXX:ASIN":"asin","TXXX:originalyear":"originalyear","UFID:http://musicbrainz.org":"musicbrainz_recordingid","TXXX:MusicBrainz Release Track Id":"musicbrainz_trackid","TXXX:MusicBrainz Album Id":"musicbrainz_albumid","TXXX:MusicBrainz Artist Id":"musicbrainz_artistid","TXXX:MusicBrainz Album Artist Id":"musicbrainz_albumartistid","TXXX:MusicBrainz Release Group Id":"musicbrainz_releasegroupid","TXXX:MusicBrainz Work Id":"musicbrainz_workid","TXXX:MusicBrainz TRM Id":"musicbrainz_trmid","TXXX:MusicBrainz Disc Id":"musicbrainz_discid","TXXX:ACOUSTID_ID":"acoustid_id","TXXX:Acoustid Id":"acoustid_id","TXXX:Acoustid Fingerprint":"acoustid_fingerprint","TXXX:MusicIP PUID":"musicip_puid","TXXX:MusicMagic Fingerprint":"musicip_fingerprint",WOAR:"website",TDRC:"date",TYER:"year",TDOR:"originaldate","TIPL:arranger":"arranger","TIPL:engineer":"engineer","TIPL:producer":"producer","TIPL:DJ-mix":"djmixer","TIPL:mix":"mixer",TMOO:"mood",SYLT:"lyrics",TSST:"discsubtitle",TKEY:"key",COMM:"comment",TOPE:"originalartist","PRIV:AverageLevel":"averageLevel","PRIV:PeakLevel":"peakLevel","TXXX:DISCOGS_ARTIST_ID":"discogs_artist_id","TXXX:DISCOGS_ARTISTS":"artists","TXXX:DISCOGS_ARTIST_NAME":"artists","TXXX:DISCOGS_ALBUM_ARTISTS":"albumartist","TXXX:DISCOGS_CATALOG":"catalognumber","TXXX:DISCOGS_COUNTRY":"releasecountry","TXXX:DISCOGS_DATE":"originaldate","TXXX:DISCOGS_LABEL":"label","TXXX:DISCOGS_LABEL_ID":"discogs_label_id","TXXX:DISCOGS_MASTER_RELEASE_ID":"discogs_master_release_id","TXXX:DISCOGS_RATING":"discogs_rating","TXXX:DISCOGS_RELEASED":"date","TXXX:DISCOGS_RELEASE_ID":"discogs_release_id","TXXX:DISCOGS_VOTES":"discogs_votes","TXXX:CATALOGID":"catalognumber","TXXX:STYLE":"genre","TXXX:replaygain_track_peak":"replaygain_track_peak","TXXX:replaygain_track_gain":"replaygain_track_gain"};class s extends n.CommonTagMapper{static toRating(e){return{source:e.email,rating:e.rating>0?(e.rating-1)/254*n.CommonTagMapper.maxRatingScore:void 0}}constructor(){super(["ID3v2.3","ID3v2.4"],a)}postMap(e){switch(e.id){case"UFID":"http://musicbrainz.org"===e.value.owner_identifier&&(e.id+=":"+e.value.owner_identifier,e.value=i.default.decodeString(e.value.identifier,"iso-8859-1"));break;case"PRIV":switch(e.value.owner_identifier){case"AverageLevel":case"PeakValue":e.id+=":"+e.value.owner_identifier,e.value=4===e.value.data.length?e.value.data.readUInt32LE(0):null}break;case"COMM":e.value=e.value?e.value.text:null;break;case"POPM":e.value=s.toRating(e.value)}}}t.ID3v24TagMapper=s},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(8),i={Title:"title",Author:"artist","WM/AlbumArtist":"albumartist","WM/AlbumTitle":"album","WM/Year":"date","WM/OriginalReleaseTime":"originaldate","WM/OriginalReleaseYear":"originalyear",Description:"comment","WM/TrackNumber":"track","WM/PartOfSet":"disk","WM/Genre":"genre","WM/Composer":"composer","WM/Lyrics":"lyrics","WM/AlbumSortOrder":"albumsort","WM/TitleSortOrder":"titlesort","WM/ArtistSortOrder":"artistsort","WM/AlbumArtistSortOrder":"albumartistsort","WM/ComposerSortOrder":"composersort","WM/Writer":"lyricist","WM/Conductor":"conductor","WM/ModifiedBy":"remixer","WM/Engineer":"engineer","WM/Producer":"producer","WM/DJMixer":"djmixer","WM/Mixer":"mixer","WM/Publisher":"label","WM/ContentGroupDescription":"grouping","WM/SubTitle":"subtitle","WM/SetSubTitle":"discsubtitle","WM/IsCompilation":"compilation","WM/SharedUserRating":"rating","WM/BeatsPerMinute":"bpm","WM/Mood":"mood","WM/Media":"media","WM/CatalogNo":"catalognumber","MusicBrainz/Album Status":"releasestatus","MusicBrainz/Album Type":"releasetype","MusicBrainz/Album Release Country":"releasecountry","WM/Script":"script","WM/Language":"language",Copyright:"copyright",LICENSE:"license","WM/EncodedBy":"encodedby","WM/EncodingSettings":"encodersettings","WM/Barcode":"barcode","WM/ISRC":"isrc","MusicBrainz/Track Id":"musicbrainz_recordingid","MusicBrainz/Release Track Id":"musicbrainz_trackid","MusicBrainz/Album Id":"musicbrainz_albumid","MusicBrainz/Artist Id":"musicbrainz_artistid","MusicBrainz/Album Artist Id":"musicbrainz_albumartistid","MusicBrainz/Release Group Id":"musicbrainz_releasegroupid","MusicBrainz/Work Id":"musicbrainz_workid","MusicBrainz/TRM Id":"musicbrainz_trmid","MusicBrainz/Disc Id":"musicbrainz_discid","Acoustid/Id":"acoustid_id","Acoustid/Fingerprint":"acoustid_fingerprint","MusicIP/PUID":"musicip_puid","WM/ARTISTS":"artists","WM/InitialKey":"key",ASIN:"asin","WM/Work":"work","WM/AuthorURL":"website","WM/Picture":"picture"};class a extends n.CommonTagMapper{static toRating(e){return{rating:parseFloat(e+1)/5}}constructor(){super(["asf"],i)}postMap(e){switch(e.id){case"WM/SharedUserRating":const t=e.id.split(":");e.value=a.toRating(e.value),e.id=t[0]}}}t.AsfTagMapper=a},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(8);t.id3v22TagMap={TT2:"title",TP1:"artist",TP2:"albumartist",TAL:"album",TYE:"year",COM:"comment",TRK:"track",TPA:"disk",TCO:"genre",PIC:"picture",TCM:"composer",TOR:"originaldate",TOT:"work",TXT:"lyricist",TP3:"conductor",TPB:"label",TT1:"grouping",TT3:"subtitle",TLA:"language",TCR:"copyright",WCP:"license",TEN:"encodedby",TSS:"encodersettings",WAR:"website","COM:iTunPGAP":"gapless"};t.ID3v22TagMapper=class extends n.CommonTagMapper{constructor(){super(["ID3v2.2"],t.id3v22TagMap)}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(8),i={Title:"title",Artist:"artist",Artists:"artists","Album Artist":"albumartist",Album:"album",Year:"date",Originalyear:"originalyear",Originaldate:"originaldate",Comment:"comment",Track:"track",Disc:"disk",DISCNUMBER:"disk",Genre:"genre","Cover Art (Front)":"picture","Cover Art (Back)":"picture",Composer:"composer",Lyrics:"lyrics",ALBUMSORT:"albumsort",TITLESORT:"titlesort",WORK:"work",ARTISTSORT:"artistsort",ALBUMARTISTSORT:"albumartistsort",COMPOSERSORT:"composersort",Lyricist:"lyricist",Writer:"writer",Conductor:"conductor",MixArtist:"remixer",Arranger:"arranger",Engineer:"engineer",Producer:"producer",DJMixer:"djmixer",Mixer:"mixer",Label:"label",Grouping:"grouping",Subtitle:"subtitle",DiscSubtitle:"discsubtitle",Compilation:"compilation",BPM:"bpm",Mood:"mood",Media:"media",CatalogNumber:"catalognumber",MUSICBRAINZ_ALBUMSTATUS:"releasestatus",MUSICBRAINZ_ALBUMTYPE:"releasetype",RELEASECOUNTRY:"releasecountry",Script:"script",Language:"language",Copyright:"copyright",LICENSE:"license",EncodedBy:"encodedby",EncoderSettings:"encodersettings",Barcode:"barcode",ISRC:"isrc",ASIN:"asin",musicbrainz_trackid:"musicbrainz_recordingid",musicbrainz_releasetrackid:"musicbrainz_trackid",MUSICBRAINZ_ALBUMID:"musicbrainz_albumid",MUSICBRAINZ_ARTISTID:"musicbrainz_artistid",MUSICBRAINZ_ALBUMARTISTID:"musicbrainz_albumartistid",MUSICBRAINZ_RELEASEGROUPID:"musicbrainz_releasegroupid",MUSICBRAINZ_WORKID:"musicbrainz_workid",MUSICBRAINZ_TRMID:"musicbrainz_trmid",MUSICBRAINZ_DISCID:"musicbrainz_discid",Acoustid_Id:"acoustid_id",ACOUSTID_FINGERPRINT:"acoustid_fingerprint",MUSICIP_PUID:"musicip_puid",Weblink:"website"};t.APEv2TagMapper=class extends n.CommonTagMapper{constructor(){const e={};for(const t in i)e[t.toUpperCase()]=i[t];super(["APEv2"],e)}getCommonName(e){return this.tagMap[e.toUpperCase()]}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(8),i={"©nam":"title","©ART":"artist",aART:"albumartist","----:com.apple.iTunes:Band":"albumartist","©alb":"album","©day":"date","©cmt":"comment",trkn:"track",disk:"disk","©gen":"genre",covr:"picture","©wrt":"composer","©lyr":"lyrics",soal:"albumsort",sonm:"titlesort",soar:"artistsort",soaa:"albumartistsort",soco:"composersort","----:com.apple.iTunes:LYRICIST":"lyricist","----:com.apple.iTunes:CONDUCTOR":"conductor","----:com.apple.iTunes:REMIXER":"remixer","----:com.apple.iTunes:ENGINEER":"engineer","----:com.apple.iTunes:PRODUCER":"producer","----:com.apple.iTunes:DJMIXER":"djmixer","----:com.apple.iTunes:MIXER":"mixer","----:com.apple.iTunes:LABEL":"label","©grp":"grouping","----:com.apple.iTunes:SUBTITLE":"subtitle","----:com.apple.iTunes:DISCSUBTITLE":"discsubtitle",cpil:"compilation",tmpo:"bpm","----:com.apple.iTunes:MOOD":"mood","----:com.apple.iTunes:MEDIA":"media","----:com.apple.iTunes:CATALOGNUMBER":"catalognumber",tvsh:"tvShow",tvsn:"tvSeason",tves:"tvEpisode",sosn:"tvShowSort",tven:"tvEpisodeId",tvnn:"tvNetwork",pcst:"podcast",purl:"podcasturl","----:com.apple.iTunes:MusicBrainz Album Status":"releasestatus","----:com.apple.iTunes:MusicBrainz Album Type":"releasetype","----:com.apple.iTunes:MusicBrainz Album Release Country":"releasecountry","----:com.apple.iTunes:SCRIPT":"script","----:com.apple.iTunes:LANGUAGE":"language",cprt:"copyright","----:com.apple.iTunes:LICENSE":"license","©too":"encodedby",pgap:"gapless","----:com.apple.iTunes:BARCODE":"barcode","----:com.apple.iTunes:ISRC":"isrc","----:com.apple.iTunes:ASIN":"asin","----:com.apple.iTunes:NOTES":"comment","----:com.apple.iTunes:MusicBrainz Track Id":"musicbrainz_recordingid","----:com.apple.iTunes:MusicBrainz Release Track Id":"musicbrainz_trackid","----:com.apple.iTunes:MusicBrainz Album Id":"musicbrainz_albumid","----:com.apple.iTunes:MusicBrainz Artist Id":"musicbrainz_artistid","----:com.apple.iTunes:MusicBrainz Album Artist Id":"musicbrainz_albumartistid","----:com.apple.iTunes:MusicBrainz Release Group Id":"musicbrainz_releasegroupid","----:com.apple.iTunes:MusicBrainz Work Id":"musicbrainz_workid","----:com.apple.iTunes:MusicBrainz TRM Id":"musicbrainz_trmid","----:com.apple.iTunes:MusicBrainz Disc Id":"musicbrainz_discid","----:com.apple.iTunes:Acoustid Id":"acoustid_id","----:com.apple.iTunes:Acoustid Fingerprint":"acoustid_fingerprint","----:com.apple.iTunes:MusicIP PUID":"musicip_puid","----:com.apple.iTunes:fingerprint":"musicip_fingerprint",gnre:"genre","----:com.apple.iTunes:ALBUMARTISTSORT":"albumartistsort","----:com.apple.iTunes:ARTISTS":"artists","----:com.apple.iTunes:ORIGINALDATE":"originaldate","----:com.apple.iTunes:ORIGINALYEAR":"originalyear",desc:"description",ldes:"description"};t.tagType="iTunes";t.MP4TagMapper=class extends n.CommonTagMapper{constructor(){super([t.tagType],i)}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(8),i={TITLE:"title",ARTIST:"artist",ARTISTS:"artists",ALBUMARTIST:"albumartist",ALBUM:"album",DATE:"date",ORIGINALDATE:"originaldate",ORIGINALYEAR:"originalyear",COMMENT:"comment",TRACKNUMBER:"track",DISCNUMBER:"disk",GENRE:"genre",METADATA_BLOCK_PICTURE:"picture",COMPOSER:"composer",LYRICS:"lyrics",ALBUMSORT:"albumsort",TITLESORT:"titlesort",WORK:"work",ARTISTSORT:"artistsort",ALBUMARTISTSORT:"albumartistsort",COMPOSERSORT:"composersort",LYRICIST:"lyricist",WRITER:"writer",CONDUCTOR:"conductor",REMIXER:"remixer",ARRANGER:"arranger",ENGINEER:"engineer",PRODUCER:"producer",DJMIXER:"djmixer",MIXER:"mixer",LABEL:"label",GROUPING:"grouping",SUBTITLE:"subtitle",DISCSUBTITLE:"discsubtitle",TRACKTOTAL:"totaltracks",DISCTOTAL:"totaldiscs",COMPILATION:"compilation",RATING:"rating",BPM:"bpm",MOOD:"mood",MEDIA:"media",CATALOGNUMBER:"catalognumber",RELEASESTATUS:"releasestatus",RELEASETYPE:"releasetype",RELEASECOUNTRY:"releasecountry",SCRIPT:"script",LANGUAGE:"language",COPYRIGHT:"copyright",LICENSE:"license",ENCODEDBY:"encodedby",ENCODERSETTINGS:"encodersettings",BARCODE:"barcode",ISRC:"isrc",ASIN:"asin",MUSICBRAINZ_TRACKID:"musicbrainz_recordingid",MUSICBRAINZ_RELEASETRACKID:"musicbrainz_trackid",MUSICBRAINZ_ALBUMID:"musicbrainz_albumid",MUSICBRAINZ_ARTISTID:"musicbrainz_artistid",MUSICBRAINZ_ALBUMARTISTID:"musicbrainz_albumartistid",MUSICBRAINZ_RELEASEGROUPID:"musicbrainz_releasegroupid",MUSICBRAINZ_WORKID:"musicbrainz_workid",MUSICBRAINZ_TRMID:"musicbrainz_trmid",MUSICBRAINZ_DISCID:"musicbrainz_discid",ACOUSTID_ID:"acoustid_id",ACOUSTID_ID_FINGERPRINT:"acoustid_fingerprint",MUSICIP_PUID:"musicip_puid",WEBSITE:"website",NOTES:"notes",TOTALTRACKS:"totaltracks",TOTALDISCS:"totaldiscs",DISCOGS_ARTIST_ID:"discogs_artist_id",DISCOGS_ARTISTS:"artists",DISCOGS_ARTIST_NAME:"artists",DISCOGS_ALBUM_ARTISTS:"albumartist",DISCOGS_CATALOG:"catalognumber",DISCOGS_COUNTRY:"releasecountry",DISCOGS_DATE:"originaldate",DISCOGS_LABEL:"label",DISCOGS_LABEL_ID:"discogs_label_id",DISCOGS_MASTER_RELEASE_ID:"discogs_master_release_id",DISCOGS_RATING:"discogs_rating",DISCOGS_RELEASED:"date",DISCOGS_RELEASE_ID:"discogs_release_id",DISCOGS_VOTES:"discogs_votes",CATALOGID:"catalognumber",STYLE:"genre",REPLAYGAIN_TRACK_GAIN:"replaygain_track_gain",REPLAYGAIN_TRACK_PEAK:"replaygain_track_peak"};class a extends n.CommonTagMapper{static toRating(e,t){return{source:e?e.toLowerCase():e,rating:parseFloat(t)*n.CommonTagMapper.maxRatingScore}}constructor(){super(["vorbis"],i)}postMap(e){if(0===e.id.indexOf("RATING:")){const t=e.id.split(":");e.value=a.toRating(t[1],e.value),e.id=t[0]}}}t.VorbisTagMapper=a},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(8);t.riffInfoTagMap={IART:"artist",ICRD:"date",INAM:"title",TITL:"title",IPRD:"album",ITRK:"track",COMM:"comment",ICMT:"comment",ICNT:"releasecountry",GNRE:"genre",IWRI:"writer",RATE:"rating",YEAR:"year",ISFT:"encodedby",CODE:"encodedby",TURL:"website",IGNR:"genre",IENG:"engineer",ITCH:"technician",IMED:"media",IRPD:"album"};t.RiffInfoTagMapper=class extends n.CommonTagMapper{constructor(){super(["exif"],t.riffInfoTagMap)}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(2),a=r(10),s=r(21),o=r(17),c=r(5),u=r(7),l=r(225),d=r(226),f=r(26),p=i("music-metadata:parser:aiff");t.AIFFParser=class extends u.BasicParser{async parse(){if("FORM"!==(await this.tokenizer.readToken(d.Header)).chunkID)throw new Error("Invalid Chunk-ID, expected 'FORM'");const e=await this.tokenizer.readToken(c.FourCcToken);switch(e){case"AIFF":this.metadata.setFormat("container",e),this.isCompressed=!1;break;case"AIFC":this.metadata.setFormat("container","AIFF-C"),this.isCompressed=!0;break;default:throw Error("Unsupported AIFF type: "+e)}this.metadata.setFormat("lossless",!this.isCompressed);try{for(;;){const e=await this.tokenizer.readToken(d.Header);p(`Chunk id=${e.chunkID}`);const t=2*Math.round(e.chunkSize/2),r=await this.readData(e);await this.tokenizer.ignore(t-r)}}catch(e){if(e.message!==a.endOfFile)throw e}}async readData(e){switch(e.chunkID){case"COMM":const t=await this.tokenizer.readToken(new l.Common(e,this.isCompressed));return this.metadata.setFormat("bitsPerSample",t.sampleSize),this.metadata.setFormat("sampleRate",t.sampleRate),this.metadata.setFormat("numberOfChannels",t.numChannels),this.metadata.setFormat("numberOfSamples",t.numSampleFrames),this.metadata.setFormat("duration",t.numSampleFrames/t.sampleRate),this.metadata.setFormat("codec",t.compressionName),e.chunkSize;case"ID3 ":const r=await this.tokenizer.readToken(new n.BufferType(e.chunkSize)),i=new f.ID3Stream(r),a=s.fromStream(i);return await(new o.ID3v2Parser).parse(this.metadata,a,this.options),e.chunkSize;case"SSND":return this.metadata.format.duration&&this.metadata.setFormat("bitrate",8*e.chunkSize/this.metadata.format.duration),0;default:return 0}}}},function(e,t,r){"use strict";(function(e){Object.defineProperty(t,"__esModule",{value:!0});const n=r(2),i=r(1),a=r(4),s=r(18),o=n("music-metadata:id3v2:frame-parser"),c="iso-8859-1";class u{static readData(t,r,n,l){const{encoding:d,bom:f}=s.TextEncodingToken.get(t,0),p=t.length;let h=0,m=[];const g=u.getNullTerminatorLength(d);let b;const y={};switch(o(`Parsing tag type=${r}, encoding=${d}, bom=${f}`),"TXXX"!==r&&"T"===r[0]?"T*":r){case"T*":case"IPLS":const w=a.default.decodeString(t.slice(1),d).replace(/\x00+$/,"");switch(r){case"TMCL":case"TIPL":case"IPLS":m=u.splitValue(4,w),m=u.functionList(m);break;case"TRK":case"TRCK":case"TPOS":m=w;break;case"TCOM":case"TEXT":case"TOLY":case"TOPE":case"TPE1":case"TSRC":m=u.splitValue(n,w);break;default:m=n>=4?u.splitValue(n,w):[w]}break;case"TXXX":m={description:(m=u.readIdentifierAndData(t,h+1,p,d)).id,text:u.splitValue(n,a.default.decodeString(m.data,d).replace(/\x00+$/,""))};break;case"PIC":case"APIC":if(l){const r={};switch(h+=1,n){case 2:r.format=a.default.decodeString(t.slice(h,h+3),d),h+=3;break;case 3:case 4:b=a.default.findZero(t,h,p,c),r.format=a.default.decodeString(t.slice(h,b),c),h=b+1;break;default:throw new Error("Warning: unexpected major versionIndex: "+n)}r.format=u.fixPictureMimeType(r.format),r.type=s.AttachedPictureType[t[h]],h+=1,b=a.default.findZero(t,h,p,d),r.description=a.default.decodeString(t.slice(h,b),d),h=b+g,r.data=e.from(t.slice(h,p)),m=r}break;case"CNT":case"PCNT":m=i.UINT32_BE.get(t,0);break;case"SYLT":for(h+=7,m=[];h=5?t.readUInt32BE(h+1):void 0};break;case"GEOB":{b=a.default.findZero(t,h+1,p,d);const e=a.default.decodeString(t.slice(h+1,b),c);h=b+1,b=a.default.findZero(t,h,p-h,d);const r=a.default.decodeString(t.slice(h+1,b),c);h=b+1,b=a.default.findZero(t,h,p-h,d),m={type:e,filename:r,description:a.default.decodeString(t.slice(h+1,b),c),data:t.slice(h+1,p)};break}case"WCOM":case"WCOP":case"WOAF":case"WOAR":case"WOAS":case"WORS":case"WPAY":case"WPUB":m=a.default.decodeString(t.slice(h,b),d);break;case"WXXX":{b=a.default.findZero(t,h+1,p,d);const e=a.default.decodeString(t.slice(h+1,b),c);h=b+1,m={description:e,url:a.default.decodeString(t.slice(h,p-h),d)};break}case"MCDI":m=t.slice(0,p);break;default:o("Warning: unsupported id3v2-tag-type: "+r)}return m}static fixPictureMimeType(e){switch(e=e.toLocaleLowerCase()){case"jpg":return"image/jpeg";case"png":return"image/png"}return e}static functionList(e){const t={};for(let r=0;r+1=4?/\x00/g:/\//g);return u.trimArray(r)}static trimArray(e){for(let t=0;t=r,`COMMON CHUNK size should always be at least ${r}`),this.len=e.chunkSize}get(e,t){const r=e.readUInt16BE(t+8)-16398,i=e.readUInt16BE(t+8+2),s={numChannels:e.readUInt16BE(t),numSampleFrames:e.readUInt32BE(t+2),sampleSize:e.readUInt16BE(t+6),sampleRate:r<0?i>>Math.abs(r):i<22){const r=e.readInt8(t+22);if(23+r+(r+1)%2!==this.len)throw new Error("Illegal pstring length");s.compressionName=new n.StringType(r,"binary").get(e,t+23)}}else s.compressionName="PCM";return s}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(5);t.Header={len:8,get:(e,t)=>({chunkID:n.FourCcToken.get(e,t),chunkSize:e.readUInt32BE(t+4)})}},function(e,t){},function(e,t,r){"use strict";var n=r(29).Buffer,i=r(229);e.exports=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.head=null,this.tail=null,this.length=0}return e.prototype.push=function(e){var t={data:e,next:null};this.length>0?this.tail.next=t:this.head=t,this.tail=t,++this.length},e.prototype.unshift=function(e){var t={data:e,next:this.head};0===this.length&&(this.tail=t),this.head=t,++this.length},e.prototype.shift=function(){if(0!==this.length){var e=this.head.data;return 1===this.length?this.head=this.tail=null:this.head=this.head.next,--this.length,e}},e.prototype.clear=function(){this.head=this.tail=null,this.length=0},e.prototype.join=function(e){if(0===this.length)return"";for(var t=this.head,r=""+t.data;t=t.next;)r+=e+t.data;return r},e.prototype.concat=function(e){if(0===this.length)return n.alloc(0);if(1===this.length)return this.head.data;for(var t,r,i,a=n.allocUnsafe(e>>>0),s=this.head,o=0;s;)t=s.data,r=a,i=o,t.copy(r,i),o+=s.data.length,s=s.next;return a},e}(),i&&i.inspect&&i.inspect.custom&&(e.exports.prototype[i.inspect.custom]=function(){var e=i.inspect({length:this.length});return this.constructor.name+" "+e})},function(e,t){},function(e,t,r){(function(e){var n=void 0!==e&&e||"undefined"!=typeof self&&self||window,i=Function.prototype.apply;function a(e,t){this._id=e,this._clearFn=t}t.setTimeout=function(){return new a(i.call(setTimeout,n,arguments),clearTimeout)},t.setInterval=function(){return new a(i.call(setInterval,n,arguments),clearInterval)},t.clearTimeout=t.clearInterval=function(e){e&&e.close()},a.prototype.unref=a.prototype.ref=function(){},a.prototype.close=function(){this._clearFn.call(n,this._id)},t.enroll=function(e,t){clearTimeout(e._idleTimeoutId),e._idleTimeout=t},t.unenroll=function(e){clearTimeout(e._idleTimeoutId),e._idleTimeout=-1},t._unrefActive=t.active=function(e){clearTimeout(e._idleTimeoutId);var t=e._idleTimeout;t>=0&&(e._idleTimeoutId=setTimeout(function(){e._onTimeout&&e._onTimeout()},t))},r(231),t.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,t.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(this,r(12))},function(e,t,r){(function(e,t){!function(e,r){"use strict";if(!e.setImmediate){var n,i,a,s,o,c=1,u={},l=!1,d=e.document,f=Object.getPrototypeOf&&Object.getPrototypeOf(e);f=f&&f.setTimeout?f:e,"[object process]"==={}.toString.call(e.process)?n=function(e){t.nextTick(function(){h(e)})}:!function(){if(e.postMessage&&!e.importScripts){var t=!0,r=e.onmessage;return e.onmessage=function(){t=!1},e.postMessage("","*"),e.onmessage=r,t}}()?e.MessageChannel?((a=new MessageChannel).port1.onmessage=function(e){h(e.data)},n=function(e){a.port2.postMessage(e)}):d&&"onreadystatechange"in d.createElement("script")?(i=d.documentElement,n=function(e){var t=d.createElement("script");t.onreadystatechange=function(){h(e),t.onreadystatechange=null,i.removeChild(t),t=null},i.appendChild(t)}):n=function(e){setTimeout(h,0,e)}:(s="setImmediate$"+Math.random()+"$",o=function(t){t.source===e&&"string"==typeof t.data&&0===t.data.indexOf(s)&&h(+t.data.slice(s.length))},e.addEventListener?e.addEventListener("message",o,!1):e.attachEvent("onmessage",o),n=function(t){e.postMessage(s+t,"*")}),f.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),r=0;r>1}}function s(e,t){return 0!=(e&1<({ID:i.FourCcToken.get(e,t),version:n.UINT32_LE.get(e,t+4)/1e3,descriptorBytes:n.UINT32_LE.get(e,t+8),headerBytes:n.UINT32_LE.get(e,t+12),seekTableBytes:n.UINT32_LE.get(e,t+16),headerDataBytes:n.UINT32_LE.get(e,t+20),apeFrameDataBytes:n.UINT32_LE.get(e,t+24),apeFrameDataBytesHigh:n.UINT32_LE.get(e,t+28),terminatingDataBytes:n.UINT32_LE.get(e,t+32),fileMD5:new n.BufferType(16).get(e,t+36)})},t.Header={len:24,get:(e,t)=>({compressionLevel:n.UINT16_LE.get(e,t),formatFlags:n.UINT16_LE.get(e,t+2),blocksPerFrame:n.UINT32_LE.get(e,t+4),finalFrameBlocks:n.UINT32_LE.get(e,t+8),totalFrames:n.UINT32_LE.get(e,t+12),bitsPerSample:n.UINT16_LE.get(e,t+16),channel:n.UINT16_LE.get(e,t+18),sampleRate:n.UINT32_LE.get(e,t+20)})},t.TagFooter={len:32,get:(e,t)=>({ID:new n.StringType(8,"ascii").get(e,t),version:n.UINT32_LE.get(e,t+8),size:n.UINT32_LE.get(e,t+12),fields:n.UINT32_LE.get(e,t+16),flags:a(n.UINT32_LE.get(e,t+20))})},t.TagItemHeader={len:8,get:(e,t)=>({size:n.UINT32_LE.get(e,t),flags:a(n.UINT32_LE.get(e,t+4))})},t.TagField=e=>new n.BufferType(e.size-t.TagFooter.len),t.parseTagFlags=a,t.isBitSet=s},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(42),i=r(241),a=r(2),s=r(7),o=a("music-metadata:parser:ASF"),c="asf";t.AsfParser=class extends s.BasicParser{async parse(){const e=await this.tokenizer.readToken(i.TopLevelHeaderObjectToken);if(!e.objectId.equals(n.default.HeaderObject))throw new Error("expected asf header; but was not found; got: "+e.objectId.str);try{await this.parseObjectHeader(e.numberOfHeaderObjects)}catch(e){o("Error while parsing ASF: %s",e)}}async parseObjectHeader(e){let t;do{const e=await this.tokenizer.readToken(i.HeaderObjectToken);switch(o("header GUID=%s",e.objectId.str),e.objectId.str){case i.FilePropertiesObject.guid.str:const r=await this.tokenizer.readToken(new i.FilePropertiesObject(e));this.metadata.setFormat("duration",r.playDuration/1e7),this.metadata.setFormat("bitrate",r.maximumBitrate);break;case i.StreamPropertiesObject.guid.str:const a=await this.tokenizer.readToken(new i.StreamPropertiesObject(e));this.metadata.setFormat("container","ASF/"+a.streamType);break;case i.HeaderExtensionObject.guid.str:const s=await this.tokenizer.readToken(new i.HeaderExtensionObject);await this.parseExtensionObject(s.extensionDataSize);break;case i.ContentDescriptionObjectState.guid.str:t=await this.tokenizer.readToken(new i.ContentDescriptionObjectState(e)),this.addTags(t);break;case i.ExtendedContentDescriptionObjectState.guid.str:t=await this.tokenizer.readToken(new i.ExtendedContentDescriptionObjectState(e)),this.addTags(t);break;case n.default.CodecListObject.str:case n.default.StreamBitratePropertiesObject.str:await this.tokenizer.ignore(e.objectSize-i.HeaderObjectToken.len);break;case n.default.PaddingObject.str:o("Padding: %s bytes",e.objectSize-i.HeaderObjectToken.len),await this.tokenizer.ignore(e.objectSize-i.HeaderObjectToken.len);break;default:this.warnings.push("Ignore ASF-Object-GUID: "+e.objectId.str),o("Ignore ASF-Object-GUID: %s",e.objectId.str),await this.tokenizer.readToken(new i.IgnoreObjectState(e))}}while(--e)}addTags(e){e.forEach(e=>{this.metadata.addTag(c,e.id,e.value)})}async parseExtensionObject(e){do{const t=await this.tokenizer.readToken(i.HeaderObjectToken);switch(t.objectId.str){case i.ExtendedStreamPropertiesObjectState.guid.str:await this.tokenizer.readToken(new i.ExtendedStreamPropertiesObjectState(t));break;case i.MetadataObjectState.guid.str:const e=await this.tokenizer.readToken(new i.MetadataObjectState(t));this.addTags(e);break;case i.MetadataLibraryObjectState.guid.str:const r=await this.tokenizer.readToken(new i.MetadataLibraryObjectState(t));this.addTags(r);break;case n.default.PaddingObject.str:await this.tokenizer.ignore(t.objectSize-i.HeaderObjectToken.len);break;case n.default.CompatibilityObject.str:this.tokenizer.ignore(t.objectSize-i.HeaderObjectToken.len);break;case n.default.ASF_Index_Placeholder_Object.str:await this.tokenizer.ignore(t.objectSize-i.HeaderObjectToken.len);break;default:this.warnings.push("Ignore ASF-Object-GUID: "+t.objectId.str),await this.tokenizer.readToken(new i.IgnoreObjectState(t))}e-=t.objectSize}while(e>0)}}},function(e,t,r){"use strict";(function(e){Object.defineProperty(t,"__esModule",{value:!0});const n=r(4),i=r(1),a=r(42),s=r(242),o=r(18);!function(e){e[e.UnicodeString=0]="UnicodeString",e[e.ByteArray=1]="ByteArray",e[e.Bool=2]="Bool",e[e.DWord=3]="DWord",e[e.QWord=4]="QWord",e[e.Word=5]="Word"}(t.DataType||(t.DataType={})),t.TopLevelHeaderObjectToken={len:30,get:(e,t)=>({objectId:a.default.fromBin(new i.BufferType(16).get(e,t)),objectSize:i.UINT64_LE.get(e,t+16),numberOfHeaderObjects:i.UINT32_LE.get(e,t+24)})},t.HeaderObjectToken={len:24,get:(e,t)=>({objectId:a.default.fromBin(new i.BufferType(16).get(e,t)),objectSize:i.UINT64_LE.get(e,t+16)})};class c{constructor(e){this.len=e.objectSize-t.HeaderObjectToken.len}postProcessTag(e,t,r,n){if("WM/Picture"===t)e.push({id:t,value:b.fromBuffer(n)});else{const i=s.AsfUtil.getParserForAttr(r);if(!i)throw new Error("unexpected value headerType: "+r);e.push({id:t,value:i(n)})}}}t.State=c;t.IgnoreObjectState=class extends c{constructor(e){super(e)}get(e,t){return null}};class u extends c{constructor(e){super(e)}get(e,t){return{fileId:a.default.fromBin(e,t),fileSize:i.UINT64_LE.get(e,t+16),creationDate:i.UINT64_LE.get(e,t+24),dataPacketsCount:i.UINT64_LE.get(e,t+32),playDuration:i.UINT64_LE.get(e,t+40),sendDuration:i.UINT64_LE.get(e,t+48),preroll:i.UINT64_LE.get(e,t+56),flags:{broadcast:n.default.strtokBITSET.get(e,t+64,24),seekable:n.default.strtokBITSET.get(e,t+64,25)},minimumDataPacketSize:i.UINT32_LE.get(e,t+68),maximumDataPacketSize:i.UINT32_LE.get(e,t+72),maximumBitrate:i.UINT32_LE.get(e,t+76)}}}u.guid=a.default.FilePropertiesObject,t.FilePropertiesObject=u;class l extends c{constructor(e){super(e)}get(e,t){return{streamType:a.default.decodeMediaType(a.default.fromBin(e,t)),errorCorrectionType:a.default.fromBin(e,t+8)}}}l.guid=a.default.StreamPropertiesObject,t.StreamPropertiesObject=l;class d{constructor(){this.len=22}get(e,t){return{reserved1:a.default.fromBin(e,t),reserved2:e.readUInt16LE(t+16),extensionDataSize:e.readUInt32LE(t+18)}}}d.guid=a.default.HeaderExtensionObject,t.HeaderExtensionObject=d;class f extends c{constructor(e){super(e)}get(e,t){const r=[];let n=t+10;for(let i=0;i0){const t=f.contentDescTags[i],o=n+a;r.push({id:t,value:s.AsfUtil.parseUnicodeAttr(e.slice(n,o))}),n=o}}return r}}f.guid=a.default.ContentDescriptionObject,f.contentDescTags=["Title","Author","Copyright","Description","Rating"],t.ContentDescriptionObjectState=f;class p extends c{constructor(e){super(e)}get(e,t){const r=[],n=e.readUInt16LE(t);let i=t+2;for(let t=0;t({lastBlock:n.default.strtokBITSET.get(e,t,7),type:n.default.getBitAllignedNumber(e,t,1,7),length:i.UINT24_BE.get(e,t+1)})},d.BlockStreamInfo={len:34,get:(e,t)=>({minimumBlockSize:i.UINT16_BE.get(e,t),maximumBlockSize:i.UINT16_BE.get(e,t+2)/1e3,minimumFrameSize:i.UINT24_BE.get(e,t+4),maximumFrameSize:i.UINT24_BE.get(e,t+7),sampleRate:i.UINT24_BE.get(e,t+10)>>4,channels:n.default.getBitAllignedNumber(e,t+12,4,3)+1,bitsPerSample:n.default.getBitAllignedNumber(e,t+12,7,5)+1,totalSamples:n.default.getBitAllignedNumber(e,t+13,4,36),fileMD5:new i.BufferType(16).get(e,t+18)})};class f{constructor(e){this.data=e,this.offset=0}readInt32(){const e=i.UINT32_LE.get(this.data,this.offset);return this.offset+=4,e}readStringUtf8(){const e=this.readInt32(),t=this.data.toString("utf8",this.offset,this.offset+e);return this.offset+=e,t}}},function(e,t,r){"use strict";(function(e){Object.defineProperty(t,"__esModule",{value:!0});const n=r(2),i=r(1),a=r(7),s=r(245),o=r(44),c=r(25),u=n("music-metadata:parser:MP4"),l="iTunes",d={raw:{lossy:!1,format:"raw"},MAC3:{lossy:!0,format:"MACE 3:1"},MAC6:{lossy:!0,format:"MACE 6:1"},ima4:{lossy:!0,format:"IMA 4:1"},ulaw:{lossy:!0,format:"uLaw 2:1"},alaw:{lossy:!0,format:"uLaw 2:1"},Qclp:{lossy:!0,format:"QUALCOMM PureVoice"},".mp3":{lossy:!0,format:"MPEG-1 layer 3"},alac:{lossy:!1,format:"ALAC"},"ac-3":{lossy:!0,format:"AC-3"},mp4a:{lossy:!0,format:"MPEG-4/AAC"},mp4s:{lossy:!0,format:"MP4S"},c608:{lossy:!0,format:"CEA-608"},c708:{lossy:!0,format:"CEA-708"}};function f(e,t,r){return r.indexOf(e)===t}class p extends a.BasicParser{static read_BE_Signed_Integer(e){return i.readIntBE(e,0,e.length)}static read_BE_Unsigned_Integer(e){return i.readUIntBE(e,0,e.length)}async parse(){this.formatList=[];const e=new s.Atom({name:"mp4",length:this.tokenizer.fileSize},!1,null);await e.readAtoms(this.tokenizer,async e=>{if(e.parent)switch(e.parent.header.name){case"ilst":case"":return this.parseMetadataItemData(e);case"stbl":switch(e.header.name){case"stsd":return this.parseAtom_stsd(e.dataLen);default:u(`Ignore: stbl/${e.header.name} atom`)}}switch(e.header.name){case"ftyp":const t=await this.parseAtom_ftyp(e.dataLen);u(`ftyp: ${t.join("/")}`);const r=t.filter(f).join("/");return void this.metadata.setFormat("container",r);case"mdhd":return this.parseAtom_mdhd(e);case"mvhd":return this.parseAtom_mvhd(e);case"mdat":this.audioLengthInBytes=e.dataLen,this.calculateBitRate()}await this.tokenizer.readToken(new i.IgnoreType(e.dataLen)),u(`Ignore atom data: path=${e.atomPath}, payload-len=${e.dataLen}`)},this.tokenizer.fileSize),this.metadata.setFormat("codec",this.formatList.filter(f).join("+"))}calculateBitRate(){this.audioLengthInBytes&&this.metadata.format.duration&&this.metadata.setFormat("bitrate",8*this.audioLengthInBytes/this.metadata.format.duration)}addTag(e,t){this.metadata.addTag(l,e,t)}addWarning(e){u("Warning:"+e),this.warnings.push(e)}parseMetadataItemData(e){let t=e.header.name;return e.readAtoms(this.tokenizer,async e=>{switch(e.header.name){case"data":return this.parseValueAtom(t,e);case"name":const r=await this.tokenizer.readToken(new o.NameAtom(e.dataLen));t+=":"+r.name;break;case"mean":const n=await this.tokenizer.readToken(new o.NameAtom(e.dataLen));t+=":"+n.name;break;default:const a=await this.tokenizer.readToken(new i.BufferType(e.dataLen));this.addWarning("Unsupported meta-item: "+t+"["+e.header.name+"] => value="+a.toString("hex")+" ascii="+a.toString("ascii"))}},e.dataLen)}async parseValueAtom(t,r){const n=await this.tokenizer.readToken(new o.DataAtom(r.header.length-o.Header.len));if(0!==n.type.set)throw new Error("Unsupported type-set != 0: "+n.type.set);switch(n.type.type){case 0:switch(t){case"trkn":case"disk":const e=i.UINT8.get(n.value,3),r=i.UINT8.get(n.value,5);this.addTag(t,e+"/"+r);break;case"gnre":const a=i.UINT8.get(n.value,1),s=c.Genres[a-1];this.addTag(t,s)}break;case 1:case 18:this.addTag(t,n.value.toString("utf-8"));break;case 13:if(this.options.skipCovers)break;this.addTag(t,{format:"image/jpeg",data:e.from(n.value)});break;case 14:if(this.options.skipCovers)break;this.addTag(t,{format:"image/png",data:e.from(n.value)});break;case 21:this.addTag(t,p.read_BE_Signed_Integer(n.value));break;case 22:this.addTag(t,p.read_BE_Unsigned_Integer(n.value));break;case 65:this.addTag(t,n.value.readInt8(0));break;case 66:this.addTag(t,n.value.readInt16BE(0));break;case 67:this.addTag(t,n.value.readInt32BE(0));break;default:this.addWarning(`atom key=${t}, has unknown well-known-type (data-type): ${n.type.type}`)}}async parseAtom_mvhd(e){const t=await this.tokenizer.readToken(new o.MvhdAtom(e.dataLen));this.parse_mxhd(t)}async parseAtom_mdhd(e){const t=await this.tokenizer.readToken(new o.MdhdAtom(e.dataLen));this.parse_mxhd(t)}parse_mxhd(e){if(e.timeScale&&!this.metadata.format.duration){const t=e.duration/e.timeScale;this.metadata.setFormat("duration",t),this.calculateBitRate()}}async parseAtom_ftyp(e){const t=await this.tokenizer.readToken(o.ftyp);if((e-=o.ftyp.len)>0){const r=await this.parseAtom_ftyp(e),n=t.type.replace(/\W/g,"");return n.length>0&&r.push(n),r}return[]}async parseAtom_stsd(e){const t=await this.tokenizer.readToken(new o.StsdAtom(e)),r=[];for(const e of t.table){const t=d[e.dataFormat];t?(this.parseSoundSampleDescription(e),this.metadata.setFormat("lossless",!t.lossy),r.push(t.format)):u(`Warning: data-format '${e.dataFormat}' missing in MP4Parser.encoderDict`)}r.length>0&&this.formatList.push(r.join("/"))}parseSoundSampleDescription(e){let t=0;const r=o.SoundSampleDescriptionVersion.get(e.description,t);if(t+=o.SoundSampleDescriptionVersion.len,0===r.version||1===r.version){const r=o.SoundSampleDescriptionV0.get(e.description,t);this.metadata.setFormat("sampleRate",r.sampleRate),this.metadata.setFormat("bitsPerSample",r.sampleSize),this.metadata.setFormat("numberOfChannels",r.numAudioChannels)}else u(`Warning: sound-sample-description ${r} not implemented`)}}t.MP4Parser=p}).call(this,r(3).Buffer)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(10),i=r(2),a=r(1),s=r(44),o=i("music-metadata:parser:MP4:Atom");class c{constructor(e,t,r){this.header=e,this.extended=t,this.parent=r,this.children=[],this.atomPath=(this.parent?this.parent.atomPath+"/":"")+this.header.name,this.dataLen=this.header.length-(t?16:8)}async readAtoms(e,t,r){const i=await this.readAtom(e,t);return this.children.push(i),void 0===r?this.readAtoms(e,t,r).catch(e=>{if(e.message!==n.endOfFile)throw e;o("Reached end-of-file")}):(r-=i.header.length)>0?this.readAtoms(e,t,r):void 0}async readAtom(e,t){const r=e.position,n=await e.readToken(s.Header),i=1===n.length;i&&(n.length=await e.readToken(s.ExtendedSize));const a=new c(n,i,this);return o(`parse atom name=${a.atomPath}, extended=${a.extended}, offset=${r}, len=${a.header.length}`),await a.readData(e,t),a}async readData(e,t){switch(this.header.name){case"moov":case"udta":case"trak":case"mdia":case"minf":case"stbl":case"":case"ilst":return this.readAtoms(e,t,this.dataLen);case"meta":return await e.readToken(new a.IgnoreType(4)),this.readAtoms(e,t,this.dataLen-4);case"mdhd":case"mvhd":case"tkhd":case"stsz":case"mdat":default:return t(this)}}}t.Atom=c},function(e,t,r){"use strict";(function(e){Object.defineProperty(t,"__esModule",{value:!0});const n=r(6),i=r(1),a=r(10),s=r(2),o=r(4),c=r(24),u=r(247),l=s("music-metadata:parser:mpeg"),d=1024,f={AudioObjectTypes:["AAC Main","AAC LC","AAC SSR","AAC LTP"],SamplingFrequencies:[96e3,88200,64e3,48e3,44100,32e3,24e3,22050,16e3,12e3,11025,8e3,7350,void 0,void 0,-1]},p=[void 0,["front-center"],["front-left","front-right"],["front-center","front-left","front-right"],["front-center","front-left","front-right","back-center"],["front-center","front-left","front-right","back-left","back-right"],["front-center","front-left","front-right","back-left","back-right","LFE-channel"],["front-center","front-left","front-right","side-left","side-right","back-left","back-right","LFE-channel"]];class h{constructor(e,t){this.versionIndex=o.default.getBitAllignedNumber(e,t+1,3,2),this.layer=h.LayerDescription[o.default.getBitAllignedNumber(e,t+1,5,2)],this.versionIndex>1&&0===this.layer?this.parseAdtsHeader(e,t):this.parseMpegHeader(e,t),this.isProtectedByCRC=!o.default.isBitSet(e,t+1,7)}calcDuration(e){return e*this.calcSamplesPerFrame()/this.samplingRate}calcSamplesPerFrame(){return h.samplesInFrameTable[1===this.version?0:1][this.layer]}calculateSideInfoLength(){if(3!==this.layer)return 2;if(3===this.channelModeIndex){if(1===this.version)return 17;if(2===this.version||2.5===this.version)return 9}else{if(1===this.version)return 32;if(2===this.version||2.5===this.version)return 17}}calcSlotSize(){return[null,4,1,1][this.layer]}parseMpegHeader(e,t){this.container="MPEG",this.bitrateIndex=o.default.getBitAllignedNumber(e,t+2,0,4),this.sampRateFreqIndex=o.default.getBitAllignedNumber(e,t+2,4,2),this.padding=o.default.isBitSet(e,t+2,6),this.privateBit=o.default.isBitSet(e,t+2,7),this.channelModeIndex=o.default.getBitAllignedNumber(e,t+3,0,2),this.modeExtension=o.default.getBitAllignedNumber(e,t+3,2,2),this.isCopyrighted=o.default.isBitSet(e,t+3,4),this.isOriginalMedia=o.default.isBitSet(e,t+3,5),this.emphasis=o.default.getBitAllignedNumber(e,t+3,7,2),this.version=h.VersionID[this.versionIndex],this.channelMode=h.ChannelMode[this.channelModeIndex],this.codec="MP"+this.layer;const r=this.calcBitrate();if(!r)throw new Error("Cannot determine bit-rate");if(this.bitrate=1e3*r,this.samplingRate=this.calcSamplingRate(),null==this.samplingRate)throw new Error("Cannot determine sampling-rate")}parseAdtsHeader(e,t){l("layer=0 => ADTS"),this.version=2===this.versionIndex?4:2,this.container="ADTS/MPEG-"+this.version;const r=o.default.getBitAllignedNumber(e,t+2,0,2);this.codec="AAC",this.codecProfile=f.AudioObjectTypes[r],l(`MPEG-4 audio-codec=${this.codec}`);const n=o.default.getBitAllignedNumber(e,t+2,2,4);this.samplingRate=f.SamplingFrequencies[n],l(`sampling-rate=${this.samplingRate}`);const i=o.default.getBitAllignedNumber(e,t+2,7,3);this.mp4ChannelConfig=p[i],l(`channel-config=${this.mp4ChannelConfig.join("+")}`),this.frameLength=o.default.getBitAllignedNumber(e,t+3,6,2)<<11}calcBitrate(){if(0===this.bitrateIndex)return null;if(15===this.bitrateIndex)return null;const e=this.version.toString()+this.layer;return h.bitrate_index[this.bitrateIndex][e]}calcSamplingRate(){return 3===this.sampRateFreqIndex?null:h.sampling_rate_freq_index[this.version][this.sampRateFreqIndex]}}h.SyncByte1=255,h.SyncByte2=224,h.VersionID=[2.5,null,2,1],h.LayerDescription=[0,3,2,1],h.ChannelMode=["stereo","joint_stereo","dual_channel","mono"],h.bitrate_index={1:{11:32,12:32,13:32,21:32,22:8,23:8},2:{11:64,12:48,13:40,21:48,22:16,23:16},3:{11:96,12:56,13:48,21:56,22:24,23:24},4:{11:128,12:64,13:56,21:64,22:32,23:32},5:{11:160,12:80,13:64,21:80,22:40,23:40},6:{11:192,12:96,13:80,21:96,22:48,23:48},7:{11:224,12:112,13:96,21:112,22:56,23:56},8:{11:256,12:128,13:112,21:128,22:64,23:64},9:{11:288,12:160,13:128,21:144,22:80,23:80},10:{11:320,12:192,13:160,21:160,22:96,23:96},11:{11:352,12:224,13:192,21:176,22:112,23:112},12:{11:384,12:256,13:224,21:192,22:128,23:128},13:{11:416,12:320,13:256,21:224,22:144,23:144},14:{11:448,12:384,13:320,21:256,22:160,23:160}},h.sampling_rate_freq_index={1:{0:44100,1:48e3,2:32e3},2:{0:22050,1:24e3,2:16e3},2.5:{0:11025,1:12e3,2:8e3}},h.samplesInFrameTable=[[0,384,1152,1152],[0,384,1152,576]];const m={len:4,get:(e,t)=>new h(e,t)};t.MpegParser=class extends c.AbstractID3Parser{constructor(){super(...arguments),this.frameCount=0,this.syncFrameCount=-1,this.countSkipFrameData=0,this.totalDataLength=0,this.bitrates=[],this.calculateEofDuration=!1,this.buf_frame_header=e.alloc(4),this.syncPeek={buf:e.alloc(d),len:0}}async _parse(){this.metadata.setFormat("lossless",!1);try{let e=!1;for(;!e;)await this.sync(),e=await this.parseCommonMpegHeader()}catch(e){if(e.message!==a.endOfFile)throw e;if(this.calculateEofDuration){const e=this.frameCount*this.samplesPerFrame;this.metadata.setFormat("numberOfSamples",e);const t=e/this.metadata.format.sampleRate;l(`Calculate duration at EOF: ${t} sec.`,t),this.metadata.setFormat("duration",t)}}}finalize(){const e=this.metadata.format,t=this.metadata.native.hasOwnProperty("ID3v1");if(e.duration&&this.tokenizer.fileSize){const r=this.tokenizer.fileSize-this.mpegOffset-(t?128:0);e.codecProfile&&"V"===e.codecProfile[0]&&this.metadata.setFormat("bitrate",8*r/e.duration)}else if(this.tokenizer.fileSize&&"CBR"===e.codecProfile){const r=this.tokenizer.fileSize-this.mpegOffset-(t?128:0),n=Math.round(r/this.frame_size)*this.samplesPerFrame;this.metadata.setFormat("numberOfSamples",n);const i=n/e.sampleRate;l("Calculate CBR duration based on file size: %s",i),this.metadata.setFormat("duration",i)}}async sync(){let e=!1;for(;;){let t=0;if(this.syncPeek.len=await this.tokenizer.peekBuffer(this.syncPeek.buf,0,d,this.tokenizer.position,!0),this.syncPeek.len<=256)throw new Error(a.endOfFile);if(0===this.syncPeek.len)throw new Error(a.endOfFile);for(;;){if(e&&224==(224&this.syncPeek.buf[t]))return this.buf_frame_header[0]=h.SyncByte1,this.buf_frame_header[1]=this.syncPeek.buf[t],await this.tokenizer.ignore(t),l(`Sync at offset=${this.tokenizer.position-1}, frameCount=${this.frameCount}`),this.syncFrameCount===this.frameCount&&(l(`Re-synced MPEG stream, frameCount=${this.frameCount}`),this.frameCount=0,this.frame_size=0),void(this.syncFrameCount=this.frameCount);if(e=!1,-1===(t=this.syncPeek.buf.indexOf(h.SyncByte1,t))){if(this.syncPeek.len=2&&0===e.layer?this.parseAdts(e):this.parseAudioFrameHeader(e)}async parseAudioFrameHeader(e){this.metadata.setFormat("numberOfChannels","mono"===e.channelMode?1:2),this.metadata.setFormat("bitrate",e.bitrate),this.frameCount<2e5&&l("offset=%s MP%s bitrate=%s sample-rate=%s",this.tokenizer.position-4,e.layer,e.bitrate,e.samplingRate);const t=e.calcSlotSize();if(null===t)throw new Error("invalid slot_size");const r=e.calcSamplesPerFrame();l(`samples_per_frame=${r}`);const n=r/8*e.bitrate/e.samplingRate+(e.padding?t:0);if(this.frame_size=Math.floor(n),this.audioFrameHeader=e,this.bitrates.push(e.bitrate),1===this.frameCount)return this.offset=m.len,await this.skipSideInformation(),!1;if(3===this.frameCount){if(this.areAllSame(this.bitrates)){if(this.samplesPerFrame=r,this.metadata.setFormat("codecProfile","CBR"),this.tokenizer.fileSize)return!0}else if(this.metadata.format.duration)return!0;if(!this.options.duration)return!0}return this.options.duration&&4===this.frameCount&&(this.samplesPerFrame=r,this.calculateEofDuration=!0),this.offset=4,e.isProtectedByCRC?(await this.parseCrc(),!1):(await this.skipSideInformation(),!1)}async parseAdts(t){const r=e.alloc(3);await this.tokenizer.readBuffer(r),t.frameLength+=o.default.getBitAllignedNumber(r,0,0,11),this.tokenizer.ignore(t.frameLength-7),this.totalDataLength+=t.frameLength,this.samplesPerFrame=1024;const n=t.samplingRate/this.samplesPerFrame,i=8*(0===this.frameCount?0:this.totalDataLength/this.frameCount)*n+.5;if(this.metadata.setFormat("codecProfile",t.codecProfile),this.metadata.setFormat("bitrate",i),t.mp4ChannelConfig&&this.metadata.setFormat("numberOfChannels",t.mp4ChannelConfig.length),l(`frame-count=${this.frameCount}, size=${t.frameLength} bytes, bit-rate=${i}`),3===this.frameCount){if(!this.options.duration)return!0;this.calculateEofDuration=!0}return!1}async parseCrc(){return this.crc=await this.tokenizer.readNumber(i.INT16_BE),this.offset+=2,this.skipSideInformation()}async skipSideInformation(){const e=this.audioFrameHeader.calculateSideInfoLength();await this.tokenizer.readToken(new i.BufferType(e)),this.offset+=e,await this.readXtraInfoHeader()}async readXtraInfoHeader(){const e=await this.tokenizer.readToken(u.InfoTagHeaderTag);switch(this.offset+=u.InfoTagHeaderTag.len,e){case"Info":return this.metadata.setFormat("codecProfile","CBR"),this.readXingInfoHeader();case"Xing":const t=await this.readXingInfoHeader(),r="V"+(100-t.vbrScale)/10;return this.metadata.setFormat("codecProfile",r),null;case"Xtra":break;case"LAME":const n=await this.tokenizer.readToken(u.LameEncoderVersion);return this.offset+=u.LameEncoderVersion.len,this.metadata.setFormat("tool","LAME "+n),await this.skipFrameData(this.frame_size-this.offset),null}const t=this.frame_size-this.offset;return t<0?this.warnings.push("Frame "+this.frameCount+"corrupt: negative frameDataLeft"):await this.skipFrameData(t),null}async readXingInfoHeader(){const e=await this.tokenizer.readToken(u.XingInfoTag);if(this.offset+=u.XingInfoTag.len,this.metadata.setFormat("tool",o.default.stripNulls(e.codec)),1==(1&e.headerFlags[3])){const t=this.audioFrameHeader.calcDuration(e.numFrames);return this.metadata.setFormat("duration",t),l("Get duration from Xing header: %s",this.metadata.format.duration),e}const t=this.frame_size-this.offset;return await this.skipFrameData(t),e}async skipFrameData(e){n.ok(e>=0,"frame-data-left cannot be negative"),await this.tokenizer.readToken(new i.IgnoreType(e)),this.countSkipFrameData+=e}areAllSame(e){const t=e[0];return e.every(e=>e===t)}}}).call(this,r(3).Buffer)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1);t.InfoTagHeaderTag=new n.StringType(4,"ascii"),t.LameEncoderVersion=new n.StringType(6,"ascii"),t.XingInfoTag={len:136,get:(e,t)=>({headerFlags:new n.BufferType(4).get(e,t),numFrames:n.UINT32_BE.get(e,t+4),streamSize:n.UINT32_BE.get(e,t+8),vbrScale:n.UINT32_BE.get(e,t+112),codec:new n.StringType(9,"ascii").get(e,t+116),infoTagRevision:n.UINT8.get(e,t+125)>>4,vbrMethod:15&n.UINT8.get(e,t+125)})}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(2),i=r(1),a=r(249),s=r(251),o=r(24),c=n("music-metadata:parser:musepack");t.default=class extends o.AbstractID3Parser{async _parse(){let e;switch(await this.tokenizer.peekToken(new i.StringType(3,"binary"))){case"MP+":c("Musepack stream-version 7"),e=new s.MpcSv7Parser;break;case"MPC":c("Musepack stream-version 8"),e=new a.MpcSv8Parser;break;default:throw new Error("Invalid Musepack signature prefix")}return e.init(this.metadata,this.tokenizer,this.options),e.parse()}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(2),i=r(6),a=r(7),s=r(250),o=r(23),c=r(5),u=n("music-metadata:parser:musepack");t.MpcSv8Parser=class extends a.BasicParser{constructor(){super(...arguments),this.audioLength=0}async parse(){const e=await this.tokenizer.readToken(c.FourCcToken);return i.equal(e,"MPCK","Magic number"),this.metadata.setFormat("container","Musepack, SV8"),this.parsePacket()}async parsePacket(){const e=new s.StreamReader(this.tokenizer);for(;;){const t=await e.readPacketHeader();switch(u(`packet-header key=${t.key}, payloadLength=${t.payloadLength}`),t.key){case"SH":const r=await e.readStreamHeader(t.payloadLength);this.metadata.setFormat("numberOfSamples",r.sampleCount),this.metadata.setFormat("sampleRate",r.sampleFrequency),this.metadata.setFormat("duration",r.sampleCount/r.sampleFrequency),this.metadata.setFormat("numberOfChannels",r.channelCount);break;case"AP":this.audioLength+=t.payloadLength,await this.tokenizer.ignore(t.payloadLength);break;case"RG":case"EI":case"SO":case"ST":case"CT":await this.tokenizer.ignore(t.payloadLength);break;case"SE":return this.metadata.setFormat("bitrate",8*this.audioLength/this.metadata.format.duration),o.APEv2Parser.parseTagHeader(this.metadata,this.tokenizer,this.options);default:throw new Error(`Unexpected header: ${t.key}`)}}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(4),a=r(2)("music-metadata:parser:musepack:sv8"),s=new n.StringType(2,"binary"),o={len:5,get:(e,t)=>({crc:n.UINT32_LE.get(e,t),streamVersion:n.UINT8.get(e,t+4)})},c={len:2,get:(e,t)=>({sampleFrequency:[44100,48e3,37800,32e3][i.default.getBitAllignedNumber(e,t,0,3)],maxUsedBands:i.default.getBitAllignedNumber(e,t,3,5),channelCount:i.default.getBitAllignedNumber(e,t+1,0,4)+1,msUsed:i.default.isBitSet(e,t+1,4),audioBlockFrames:i.default.getBitAllignedNumber(e,t+1,5,3)})};t.StreamReader=class{constructor(e){this.tokenizer=e}async readPacketHeader(){const e=await this.tokenizer.readToken(s),t=await this.readVariableSizeField();return{key:e,payloadLength:t.value-2-t.len}}async readStreamHeader(e){const t={};a(`Reading SH at offset=${this.tokenizer.position}`);const r=await this.tokenizer.readToken(o);e-=o.len,Object.assign(t,r),a(`SH.streamVersion = ${r.streamVersion}`);const n=await this.readVariableSizeField();e-=n.len,t.sampleCount=n.value;const i=await this.readVariableSizeField();e-=i.len,t.beginningOfSilence=i.value;const s=await this.tokenizer.readToken(c);return e-=c.len,Object.assign(t,s),await this.tokenizer.ignore(e),t}async readVariableSizeField(e=1,t=0){let r=await this.tokenizer.readToken(n.UINT8);return 0==(128&r)?{len:e,value:t+r}:(r&=127,r+=t,this.readVariableSizeField(e+1,r<<7))}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(2),i=r(6),a=r(7),s=r(252),o=r(23),c=r(253),u=n("music-metadata:parser:musepack");t.MpcSv7Parser=class extends a.BasicParser{constructor(){super(...arguments),this.audioLength=0}async parse(){const e=await this.tokenizer.readToken(s.Header);i.equal(e.signature,"MP+","Magic number"),u(`stream-version=${e.streamMajorVersion}.${e.streamMinorVersion}`),this.metadata.setFormat("container","Musepack, SV7"),this.metadata.setFormat("sampleRate",e.sampleFrequency);const t=1152*(e.frameCount-1)+e.lastFrameLength;this.metadata.setFormat("numberOfSamples",t),this.duration=t/e.sampleFrequency,this.metadata.setFormat("duration",this.duration),this.bitreader=new c.BitReader(this.tokenizer),this.metadata.setFormat("numberOfChannels",e.midSideStereo||e.intensityStereo?2:1);const r=await this.bitreader.read(8);return this.metadata.setFormat("codec",(r/100).toFixed(2)),await this.skipAudioData(e.frameCount),u(`End of audio stream, switching to APEv2, offset=${this.tokenizer.position}`),o.APEv2Parser.parseTagHeader(this.metadata,this.tokenizer,this.options)}async skipAudioData(e){for(;e-- >0;){const e=await this.bitreader.read(20);this.audioLength+=20+e,await this.bitreader.ignore(e)}const t=await this.bitreader.read(11);this.audioLength+=t,this.metadata.setFormat("bitrate",this.audioLength/this.duration)}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(4);t.Header={len:24,get:(e,t)=>{const r={signature:e.toString("binary",t,t+3),streamMinorVersion:i.default.getBitAllignedNumber(e,t+3,0,4),streamMajorVersion:i.default.getBitAllignedNumber(e,t+3,4,4),frameCount:n.UINT32_LE.get(e,t+4),maxLevel:n.UINT16_LE.get(e,t+8),sampleFrequency:[44100,48e3,37800,32e3][i.default.getBitAllignedNumber(e,t+10,0,2)],link:i.default.getBitAllignedNumber(e,t+10,2,2),profile:i.default.getBitAllignedNumber(e,t+10,4,4),maxBand:i.default.getBitAllignedNumber(e,t+11,0,6),intensityStereo:i.default.isBitSet(e,t+11,6),midSideStereo:i.default.isBitSet(e,t+11,7),titlePeak:n.UINT16_LE.get(e,t+12),titleGain:n.UINT16_LE.get(e,t+14),albumPeak:n.UINT16_LE.get(e,t+16),albumGain:n.UINT16_LE.get(e,t+18),lastFrameLength:n.UINT32_LE.get(e,t+20)>>>20&2047,trueGapless:i.default.isBitSet(e,t+23,0)};return r.lastFrameLength=r.trueGapless?n.UINT32_LE.get(e,20)>>>20&2047:0,r}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1);t.BitReader=class{constructor(e){this.tokenizer=e,this.pos=0,this.dword=void 0}async read(e){for(;void 0===this.dword;)this.dword=await this.tokenizer.readToken(n.UINT32_LE);let t=this.dword;return this.pos+=e,this.pos<32?(t>>>=32-this.pos)&(1<>>32-this.pos),t&(1<0){const t=32-this.pos;this.dword=void 0,e-=t,this.pos=0}const t=e%32,r=(e-t)/32;return await this.tokenizer.ignore(4*r),this.read(t)}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(2),a=r(6),s=r(4),o=r(5),c=r(31),u=r(255),l=r(257),d=r(7),f=r(259),p=i("music-metadata:parser:ogg");class h{static sum(e,t,r){let n=0;for(let i=t;i0)return this.warnings.push("Invalid FourCC ID, maybe last OGG-page is not marked with last-page flag"),this.pageConsumer.flush();throw e}}}m.Header={len:27,get:(e,t)=>({capturePattern:o.FourCcToken.get(e,t),version:e.readUInt8(t+4),headerType:{continued:s.default.strtokBITSET.get(e,t+5,0),firstPage:s.default.strtokBITSET.get(e,t+5,1),lastPage:s.default.strtokBITSET.get(e,t+5,2)},absoluteGranulePosition:e.readIntLE(t+6,6),streamSerialNumber:n.UINT32_LE.get(e,t+14),pageSequenceNo:n.UINT32_LE.get(e,t+18),pageChecksum:n.UINT32_LE.get(e,t+22),page_segments:e.readUInt8(t+26)})},t.OggParser=m},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(256),a=r(31);t.OpusParser=class extends a.VorbisParser{constructor(e,t,r){super(e,t),this.tokenizer=r,this.lastPos=-1}parseFirstPage(e,t){if(this.metadata.setFormat("codec","Opus"),this.idHeader=new i.IdHeader(t.length).get(t,0),"OpusHead"!==this.idHeader.magicSignature)throw new Error("Illegal ogg/Opus magic-signature");this.metadata.setFormat("sampleRate",this.idHeader.inputSampleRate),this.metadata.setFormat("numberOfChannels",this.idHeader.channelCount)}parseFullPage(e){switch(new n.StringType(8,"ascii").get(e,0)){case"OpusTags":this.parseUserCommentList(e,8),this.lastPos=this.tokenizer.position}}calculateDuration(e){if(this.metadata.format.sampleRate&&e.absoluteGranulePosition>=0&&(this.metadata.setFormat("numberOfSamples",e.absoluteGranulePosition-this.idHeader.preSkip),this.metadata.setFormat("duration",this.metadata.format.numberOfSamples/this.idHeader.inputSampleRate),-1!==this.lastPos&&this.tokenizer.fileSize&&this.metadata.format.duration)){const e=this.tokenizer.fileSize-this.lastPos;this.metadata.setFormat("bitrate",8*e/this.metadata.format.duration)}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1);t.IdHeader=class{constructor(e){if(this.len=e,e<19)throw new Error("ID-header-page 0 should be at least 19 bytes long")}get(e,t){return{magicSignature:new n.StringType(8,"ascii").get(e,t+0),version:e.readUInt8(t+8),channelCount:e.readUInt8(t+9),preSkip:e.readInt16LE(t+10),inputSampleRate:e.readInt32LE(t+12),outputGain:e.readInt16LE(t+16),channelMapping:e.readUInt8(t+18)}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(2),i=r(258),a=r(31),s=n("music-metadata:parser:ogg:speex");t.SpeexParser=class extends a.VorbisParser{constructor(e,t,r){super(e,t),this.tokenizer=r}parseFirstPage(e,t){s("First Ogg/Speex page");const r=i.Header.get(t,0);this.metadata.setFormat("codec",`Speex ${r.version}`),this.metadata.setFormat("numberOfChannels",r.nb_channels),this.metadata.setFormat("sampleRate",r.rate),-1!==r.bitrate&&this.metadata.setFormat("bitrate",r.bitrate)}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(4);t.Header={len:80,get:(e,t)=>({speex:new n.StringType(8,"ascii").get(e,t+0),version:i.default.trimRightNull(new n.StringType(20,"ascii").get(e,t+8)),version_id:e.readInt32LE(t+28),header_size:e.readInt32LE(t+32),rate:e.readInt32LE(t+36),mode:e.readInt32LE(t+40),mode_bitstream_version:e.readInt32LE(t+44),nb_channels:e.readInt32LE(t+48),bitrate:e.readInt32LE(t+52),frame_size:e.readInt32LE(t+56),vbr:e.readInt32LE(t+60),frames_per_packet:e.readInt32LE(t+64),extra_headers:e.readInt32LE(t+68),reserved1:e.readInt32LE(t+72),reserved2:e.readInt32LE(t+76)})}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(2),i=r(260),a=n("music-metadata:parser:ogg:theora");t.TheoraParser=class{constructor(e,t,r){this.metadata=e,this.tokenizer=r}parsePage(e,t){e.headerType.firstPage&&this.parseFirstPage(e,t)}flush(){a("flush")}parseFirstPage(e,t){a("First Ogg/Theora page"),this.metadata.setFormat("codec","Theora");const r=i.IdentificationHeader.get(t,0);this.metadata.setFormat("bitrate",r.nombr)}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1);t.IdentificationHeader={len:42,get:(e,t)=>({id:new n.StringType(7,"ascii").get(e,t),vmaj:e.readUInt8(t+7),vmin:e.readUInt8(t+8),vrev:e.readUInt8(t+9),vmbw:e.readUInt16BE(t+10),vmbh:e.readUInt16BE(t+17),nombr:n.UINT24_BE.get(e,t+37),nqual:e.readUInt8(t+40)})}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(10),i=r(21),a=r(1),s=r(2),o=r(262),c=r(263),u=r(17),l=r(4),d=r(5),f=r(7),p=r(26),h=s("music-metadata:parser:RIFF");t.WaveParser=class extends f.BasicParser{async parse(){const e=await this.tokenizer.readToken(o.Header);if(h(`pos=${this.tokenizer.position}, parse: chunkID=${e.chunkID}`),"RIFF"===e.chunkID)return this.parseRiffChunk().catch(e=>{if(e.message!==n.endOfFile)throw e})}async parseRiffChunk(){const e=await this.tokenizer.readToken(d.FourCcToken);switch(this.metadata.setFormat("container",e),e){case"WAVE":return this.readWaveChunk();default:throw new Error(`Unsupported RIFF format: RIFF/${e}`)}}async readWaveChunk(){for(;;){const e=await this.tokenizer.readToken(o.Header);switch(this.header=e,h(`pos=${this.tokenizer.position}, readChunk: chunkID=RIFF/WAVE/${e.chunkID}`),e.chunkID){case"LIST":await this.parseListTag(e);break;case"fact":this.metadata.setFormat("lossless",!1),this.fact=await this.tokenizer.readToken(new c.FactChunk(e));break;case"fmt ":const t=await this.tokenizer.readToken(new c.Format(e));let r=c.WaveFormat[t.wFormatTag];r||(h("WAVE/non-PCM format="+t.wFormatTag),r="non-PCM ("+t.wFormatTag+")"),this.metadata.setFormat("codec",r),this.metadata.setFormat("bitsPerSample",t.wBitsPerSample),this.metadata.setFormat("sampleRate",t.nSamplesPerSec),this.metadata.setFormat("numberOfChannels",t.nChannels),this.metadata.setFormat("bitrate",t.nBlockAlign*t.nSamplesPerSec*8),this.blockAlign=t.nBlockAlign;break;case"id3 ":case"ID3 ":const n=await this.tokenizer.readToken(new a.BufferType(e.chunkSize)),s=new p.ID3Stream(n),o=i.fromStream(s);await(new u.ID3v2Parser).parse(this.metadata,o,this.options);break;case"data":!1!==this.metadata.format.lossless&&this.metadata.setFormat("lossless",!0);const l=this.fact?this.fact.dwSampleLength:e.chunkSize/this.blockAlign;this.metadata.setFormat("numberOfSamples",l),this.metadata.setFormat("duration",l/this.metadata.format.sampleRate),this.metadata.setFormat("bitrate",this.metadata.format.numberOfChannels*this.blockAlign*this.metadata.format.sampleRate),await this.tokenizer.ignore(e.chunkSize);break;default:h(`Ignore chunk: RIFF/${e.chunkID} of ${e.chunkSize} bytes`),this.warnings.push("Ignore chunk: RIFF/"+e.chunkID),await this.tokenizer.ignore(e.chunkSize)}this.header.chunkSize%2==1&&(h("Read odd padding byte"),await this.tokenizer.ignore(1))}}async parseListTag(e){const t=await this.tokenizer.readToken(d.FourCcToken);switch(h("pos=%s, parseListTag: chunkID=RIFF/WAVE/LIST/%s",this.tokenizer.position,t),t){case"INFO":return this.parseRiffInfoTags(e.chunkSize-4);case"adtl":default:return this.warnings.push("Ignore chunk: RIFF/WAVE/LIST/"+t),h("Ignoring chunkID=RIFF/WAVE/LIST/"+t),this.tokenizer.ignore(e.chunkSize-4)}}async parseRiffInfoTags(e){for(;e>=8;){const t=await this.tokenizer.readToken(o.Header),r=new o.ListInfoTagValue(t),n=await this.tokenizer.readToken(r);this.addTag(t.chunkID,l.default.stripNulls(n)),e-=8+r.len}if(0!==e)throw Error("Illegal remaining size: "+e)}addTag(e,t){this.metadata.addTag("exif",e,t)}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(5);t.Header={len:8,get:(e,t)=>({chunkID:i.FourCcToken.get(e,t),chunkSize:e.readUInt32LE(t+4)})};t.ListInfoTagValue=class{constructor(e){this.tagHeader=e,this.len=e.chunkSize,this.len+=1&this.len}get(e,t){return new n.StringType(this.tagHeader.chunkSize,"ascii").get(e,t)}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(6);!function(e){e[e.PCM=1]="PCM",e[e.ADPCM=2]="ADPCM",e[e.IEEE_FLOAT=3]="IEEE_FLOAT",e[e.MPEG_ADTS_AAC=5632]="MPEG_ADTS_AAC",e[e.MPEG_LOAS=5634]="MPEG_LOAS",e[e.RAW_AAC1=255]="RAW_AAC1",e[e.DOLBY_AC3_SPDIF=146]="DOLBY_AC3_SPDIF",e[e.DVM=8192]="DVM",e[e.RAW_SPORT=576]="RAW_SPORT",e[e.ESST_AC3=577]="ESST_AC3",e[e.DRM=9]="DRM",e[e.DTS2=8193]="DTS2",e[e.MPEG=80]="MPEG"}(t.WaveFormat||(t.WaveFormat={}));t.Format=class{constructor(e){n.ok(e.chunkSize>=16,"16 for PCM."),this.len=e.chunkSize}get(e,t){return{wFormatTag:e.readUInt16LE(t),nChannels:e.readUInt16LE(t+2),nSamplesPerSec:e.readUInt32LE(t+4),nAvgBytesPerSec:e.readUInt32LE(t+8),nBlockAlign:e.readUInt16LE(t+12),wBitsPerSample:e.readUInt16LE(t+14)}}};t.FactChunk=class{constructor(e){n.ok(e.chunkSize>=4,"minimum fact chunk size."),this.len=e.chunkSize}get(e,t){return{dwSampleLength:e.readUInt32LE(t)}}}},function(e,t,r){"use strict";(function(e){Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(6),a=r(23),s=r(5),o=r(7),c=r(265),u=r(2)("music-metadata:parser:WavPack");t.WavPackParser=class extends o.BasicParser{async parse(){return this.audioDataSize=0,await this.parseWavPackBlocks(),a.APEv2Parser.parseTagHeader(this.metadata,this.tokenizer,this.options)}async parseWavPackBlocks(){do{if("wvpk"!==await this.tokenizer.peekToken(s.FourCcToken))break;const e=await this.tokenizer.readToken(c.WavPack.BlockHeaderToken);i.strictEqual(e.BlockID,"wvpk","WavPack Block-ID"),u(`WavPack header blockIndex=${e.blockIndex}, len=${c.WavPack.BlockHeaderToken.len}`),0!==e.blockIndex||this.metadata.format.container||(this.metadata.setFormat("container","WavPack"),this.metadata.setFormat("lossless",!e.flags.isHybrid),this.metadata.setFormat("bitsPerSample",e.flags.bitsPerSample),e.flags.isDSD||(this.metadata.setFormat("sampleRate",e.flags.samplingRate),this.metadata.setFormat("duration",e.totalSamples/e.flags.samplingRate)),this.metadata.setFormat("numberOfChannels",e.flags.isMono?1:2),this.metadata.setFormat("numberOfSamples",e.totalSamples),this.metadata.setFormat("codec",e.flags.isDSD?"DSD":"PCM"));const t=e.blockSize-(c.WavPack.BlockHeaderToken.len-8);0===e.blockIndex?await this.parseMetadataSubBlock(e,t):await this.tokenizer.ignore(t),e.blockSamples>0&&(this.audioDataSize+=e.blockSize)}while(!this.tokenizer.fileSize||this.tokenizer.fileSize-this.tokenizer.position>=c.WavPack.BlockHeaderToken.len);this.metadata.setFormat("bitrate",8*this.audioDataSize/this.metadata.format.duration)}async parseMetadataSubBlock(t,r){for(;r>c.WavPack.MetadataIdToken.len;){const a=await this.tokenizer.readToken(c.WavPack.MetadataIdToken),s=await this.tokenizer.readNumber(a.largeBlock?n.UINT24_LE:n.UINT8),o=e.alloc(2*s-(a.isOddSize?1:0));switch(await this.tokenizer.readBuffer(o,0,o.length),u(`Metadata Sub-Blocks functionId=0x${a.functionId.toString(16)}, id.largeBlock=${a.largeBlock},data-size=${o.length}`),a.functionId){case 0:break;case 14:u("ID_DSD_BLOCK");const e=1<>>t&4294967295>>>32-r}}s.BlockHeaderToken={len:32,get:(e,t)=>{const r=n.UINT32_LE.get(e,t+24),o={BlockID:i.FourCcToken.get(e,t),blockSize:n.UINT32_LE.get(e,t+4),version:n.UINT16_LE.get(e,t+8),totalSamples:(n.UINT8.get(e,t+11)<<32)+n.UINT32_LE.get(e,t+12),blockIndex:(n.UINT8.get(e,t+10)<<32)+n.UINT32_LE.get(e,t+16),blockSamples:n.UINT32_LE.get(e,t+20),flags:{bitsPerSample:8*(1+s.getBitAllignedNumber(r,0,2)),isMono:s.isBitSet(r,2),isHybrid:s.isBitSet(r,3),isJointStereo:s.isBitSet(r,4),crossChannel:s.isBitSet(r,5),hybridNoiseShaping:s.isBitSet(r,6),floatingPoint:s.isBitSet(r,7),samplingRate:a[s.getBitAllignedNumber(r,23,4)],isDSD:s.isBitSet(r,31)},crc:new n.BufferType(4).get(e,t+28)};return o.flags.isDSD&&(o.totalSamples*=8),o}},s.MetadataIdToken={len:1,get:(e,t)=>({functionId:s.getBitAllignedNumber(e[t],0,6),isOptional:s.isBitSet(e[t],5),isOddSize:s.isBitSet(e[t],6),largeBlock:s.isBitSet(e[t],7)})},t.WavPack=s},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(24),i=r(6),a=r(2),s=r(267),o=r(17),c=a("music-metadata:parser:DSF");t.DsfParser=class extends n.AbstractID3Parser{async _parse(){const e=this.tokenizer.position,t=await this.tokenizer.readToken(s.ChunkHeader);i.strictEqual(t.id,"DSD ","Invalid chunk signature"),this.metadata.setFormat("container","DSF"),this.metadata.setFormat("lossless",!0);const r=await this.tokenizer.readToken(s.DsdChunk);if(0!==r.metadataPointer)return c(`expect ID3v2 at offset=${r.metadataPointer}`),await this.parseChunks(r.fileSize-t.size),await this.tokenizer.ignore(r.metadataPointer-this.tokenizer.position-e),(new o.ID3v2Parser).parse(this.metadata,this.tokenizer,this.options);c("No ID3v2 tag present")}async parseChunks(e){for(;e>=s.ChunkHeader.len;){const t=await this.tokenizer.readToken(s.ChunkHeader);switch(c(`Parsing chunk name=${t.id} size=${t.size}`),t.id){case"fmt ":const e=await this.tokenizer.readToken(s.FormatChunk);this.metadata.setFormat("numberOfChannels",e.channelNum),this.metadata.setFormat("sampleRate",e.samplingFrequency),this.metadata.setFormat("bitsPerSample",e.bitsPerSample),this.metadata.setFormat("numberOfSamples",e.sampleCount),this.metadata.setFormat("duration",e.sampleCount/e.samplingFrequency);const r=e.bitsPerSample*e.samplingFrequency*e.channelNum;return void this.metadata.setFormat("bitrate",r);default:this.tokenizer.ignore(t.size-s.ChunkHeader.len)}e-=t.size}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(5);t.ChunkHeader={len:12,get:(e,t)=>({id:i.FourCcToken.get(e,t),size:n.UINT64_LE.get(e,t+4)})},t.DsdChunk={len:16,get:(e,t)=>({fileSize:n.INT64_LE.get(e,t),metadataPointer:n.INT64_LE.get(e,t+8)})},function(e){e[e.mono=1]="mono",e[e.stereo=2]="stereo",e[e.channels=3]="channels",e[e.quad=4]="quad",e[e["4 channels"]=5]="4 channels",e[e["5 channels"]=6]="5 channels",e[e["5.1 channels"]=7]="5.1 channels"}(t.ChannelType||(t.ChannelType={})),t.FormatChunk={len:40,get:(e,t)=>({formatVersion:n.INT32_LE.get(e,t),formatID:n.INT32_LE.get(e,t+4),channelType:n.INT32_LE.get(e,t+8),channelNum:n.INT32_LE.get(e,t+12),samplingFrequency:n.INT32_LE.get(e,t+16),bitsPerSample:n.INT32_LE.get(e,t+20),sampleCount:n.INT64_LE.get(e,t+24),blockSizePerChannel:n.INT32_LE.get(e,t+32)})}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(6),i=r(1),a=r(2),s=r(5),o=r(7),c=r(26),u=r(269),l=r(21),d=r(17),f=a("music-metadata:parser:aiff");t.DsdiffParser=class extends o.BasicParser{async parse(){const e=await this.tokenizer.readToken(u.ChunkHeader);n.strictEqual(e.chunkID,"FRM8");const t=(await this.tokenizer.readToken(s.FourCcToken)).trim();switch(t){case"DSD":return this.metadata.setFormat("container",`DSDIFF/${t}`),this.metadata.setFormat("lossless",!0),this.readFmt8Chunks(e.chunkSize-s.FourCcToken.len);default:throw Error(`Unsupported DSDIFF type: ${t}`)}}async readFmt8Chunks(e){for(;e>=u.ChunkHeader.len;){const t=await this.tokenizer.readToken(u.ChunkHeader);f(`Chunk id=${t.chunkID}`),await this.readData(t),e-=u.ChunkHeader.len+t.chunkSize}}async readData(e){f(`Reading data of chunk[ID=${e.chunkID}, size=${e.chunkSize}]`);const t=this.tokenizer.position;switch(e.chunkID.trim()){case"FVER":const t=await this.tokenizer.readToken(i.UINT32_LE);f(`DSDIFF version=${t}`);break;case"PROP":const r=await this.tokenizer.readToken(s.FourCcToken);n.strictEqual(r,"SND "),await this.handleSoundPropertyChunks(e.chunkSize-s.FourCcToken.len);break;case"ID3":const a=await this.tokenizer.readToken(new i.BufferType(e.chunkSize)),o=new c.ID3Stream(a),u=l.fromStream(o);await(new d.ID3v2Parser).parse(this.metadata,u,this.options);break;default:f(`Ignore chunk[ID=${e.chunkID}, size=${e.chunkSize}]`);break;case"DSD":this.metadata.setFormat("numberOfSamples",8*e.chunkSize/this.metadata.format.numberOfChannels),this.metadata.setFormat("duration",this.metadata.format.numberOfSamples/this.metadata.format.sampleRate)}const r=e.chunkSize-(this.tokenizer.position-t);r>0&&(f(`After Parsing chunk, remaining ${r} bytes`),await this.tokenizer.ignore(r))}async handleSoundPropertyChunks(e){for(f(`Parsing sound-property-chunks, remainingSize=${e}`);e>0;){const t=await this.tokenizer.readToken(u.ChunkHeader);f(`Sound-property-chunk[ID=${t.chunkID}, size=${t.chunkSize}]`);const r=this.tokenizer.position;switch(t.chunkID.trim()){case"FS":const e=await this.tokenizer.readToken(i.UINT32_BE);this.metadata.setFormat("sampleRate",e);break;case"CHNL":const r=await this.tokenizer.readToken(i.UINT16_BE);this.metadata.setFormat("numberOfChannels",r),await this.handleChannelChunks(t.chunkSize-i.UINT16_BE.len);break;case"CMPR":const n=(await this.tokenizer.readToken(s.FourCcToken)).trim(),a=await this.tokenizer.readToken(i.UINT8),o=await this.tokenizer.readToken(new i.StringType(a,"ascii"));"DSD"===n&&(this.metadata.setFormat("lossless",!0),this.metadata.setFormat("bitsPerSample",1)),this.metadata.setFormat("codec",`${n} (${o})`);break;case"ABSS":const c=await this.tokenizer.readToken(i.UINT16_BE),u=await this.tokenizer.readToken(i.UINT8),l=await this.tokenizer.readToken(i.UINT8),d=await this.tokenizer.readToken(i.UINT32_BE);f(`ABSS ${c}:${u}:${l}.${d}`);break;case"LSCO":const p=await this.tokenizer.readToken(i.UINT16_BE);f(`LSCO lsConfig=${p}`);break;case"COMT":default:f(`Unknown sound-property-chunk[ID=${t.chunkID}, size=${t.chunkSize}]`),await this.tokenizer.ignore(t.chunkSize)}const n=t.chunkSize-(this.tokenizer.position-r);n>0&&(f(`After Parsing sound-property-chunk ${t.chunkSize}, remaining ${n} bytes`),await this.tokenizer.ignore(n)),e-=u.ChunkHeader.len+t.chunkSize,f(`Parsing sound-property-chunks, remainingSize=${e}`)}if(this.metadata.format.lossless&&this.metadata.format.sampleRate&&this.metadata.format.numberOfChannels&&this.metadata.format.bitsPerSample){const e=this.metadata.format.sampleRate*this.metadata.format.numberOfChannels*this.metadata.format.bitsPerSample;this.metadata.setFormat("bitrate",e)}}async handleChannelChunks(e){f(`Parsing channel-chunks, remainingSize=${e}`);const t=[];for(;e>=s.FourCcToken.len;){const r=await this.tokenizer.readToken(s.FourCcToken);f(`Channel[ID=${r}]`),t.push(r),e-=s.FourCcToken.len}return f(`Channels: ${t.join(", ")}`),t}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(5);t.ChunkHeader={len:12,get:(e,t)=>({chunkID:i.FourCcToken.get(e,t),chunkSize:n.INT64_BE.get(e,t+4)})}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(36);t.ReadableWeToNodeStream=class extends n.Readable{constructor(e){super(),this.bytesRead=0,this.released=!1,this.reader=e.getReader()}async _read(){if(this.released)return void this.push(null);this.pendingRead=this.reader.read();const e=await this.pendingRead;delete this.pendingRead,e.done||this.released?this.push(null):(this.bytesRead+=e.value.length,this.push(e.value))}async waitForReadToComplete(){this.pendingRead&&await this.pendingRead}async close(){await this.syncAndRelease()}async syncAndRelease(){this.released=!0,await this.waitForReadToComplete(),await this.reader.releaseLock()}}},function(e,t,r){(function(t){var n=r(272).strict;e.exports=function(e){if(n(e)){var r=t.from(e.buffer);return e.byteLength!==e.buffer.byteLength&&(r=r.slice(e.byteOffset,e.byteOffset+e.byteLength)),r}return t.from(e)}}).call(this,r(3).Buffer)},function(e,t){e.exports=i,i.strict=a,i.loose=s;var r=Object.prototype.toString,n={"[object Int8Array]":!0,"[object Int16Array]":!0,"[object Int32Array]":!0,"[object Uint8Array]":!0,"[object Uint8ClampedArray]":!0,"[object Uint16Array]":!0,"[object Uint32Array]":!0,"[object Float32Array]":!0,"[object Float64Array]":!0};function i(e){return a(e)||s(e)}function a(e){return e instanceof Int8Array||e instanceof Int16Array||e instanceof Int32Array||e instanceof Uint8Array||e instanceof Uint8ClampedArray||e instanceof Uint16Array||e instanceof Uint32Array||e instanceof Float32Array||e instanceof Float64Array}function s(e){return n[r.call(e)]}}]); \ No newline at end of file +!function(e){var t={};function r(n){if(t[n])return t[n].exports;var i=t[n]={i:n,l:!1,exports:{}};return e[n].call(i.exports,i,i.exports,r),i.l=!0,i.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)r.d(n,i,function(t){return e[t]}.bind(null,i));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="/",r(r.s=185)}([,function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(6),i=(e,t,r,n)=>{if(t+r>e.length){if("function"!=typeof n)throw new Error("Buffer out of space and no valid flush() function found");return n(e,t),0}return t};t.UINT8={len:1,get:(e,t)=>e.readUInt8(t),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=0&&r<=255),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeUInt8(r,s),s-t+this.len}},t.UINT16_LE={len:2,get:(e,t)=>e.readUInt16LE(t),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=0&&r<=65535),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeUInt16LE(r,s),s-t+this.len}},t.UINT16_BE={len:2,get:(e,t)=>e.readUInt16BE(t),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=0&&r<=65535),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeUInt16BE(r,s),s-t+this.len}},t.UINT24_LE={len:3,get:(e,t)=>e.readUIntLE(t,3),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=0&&r<=16777215),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeUIntLE(r,s,3),s-t+this.len}},t.UINT24_BE={len:3,get:(e,t)=>e.readUIntBE(t,3),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=0&&r<=16777215),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeUIntBE(r,s,3),s-t+this.len}},t.UINT32_LE={len:4,get:(e,t)=>e.readUInt32LE(t),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=0&&r<=4294967295),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeUInt32LE(r,s),s-t+this.len}},t.UINT32_BE={len:4,get:(e,t)=>e.readUInt32BE(t),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=0&&r<=4294967295),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeUInt32BE(r,s),s-t+this.len}},t.INT8={len:1,get:(e,t)=>e.readInt8(t),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=-128&&r<=127),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeInt8(r,s),s-t+this.len}},t.INT16_BE={len:2,get:(e,t)=>e.readInt16BE(t),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=-32768&&r<=32767),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeInt16BE(r,s),s-t+this.len}},t.INT16_LE={len:2,get:(e,t)=>e.readInt16LE(t),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=-32768&&r<=32767),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeInt16LE(r,s),s-t+this.len}},t.INT24_LE={len:3,get:(e,t)=>e.readIntLE(t,3),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=-8388608&&r<=8388607),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeIntLE(r,s,3),s-t+this.len}},t.INT24_BE={len:3,get:(e,t)=>e.readIntBE(t,3),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=-8388608&&r<=8388607),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeIntBE(r,s,3),s-t+this.len}},t.INT32_BE={len:4,get:(e,t)=>e.readInt32BE(t),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=-2147483648&&r<=2147483647),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeInt32BE(r,s),s-t+this.len}},t.INT32_LE={len:4,get:(e,t)=>e.readInt32LE(t),put(e,t,r,a){n.equal(typeof t,"number"),n.equal(typeof r,"number"),n.ok(r>=-2147483648&&r<=2147483647),n.ok(t>=0),n.ok(this.len<=e.length);const s=i(e,t,this.len,a);return e.writeInt32LE(r,s),s-t+this.len}},t.UINT64_LE={len:8,get(e,t){return function(e,t,r){r>>>=0;let n=e[t>>>=0],i=1,a=0;for(;++a>>=0;let i=1,a=0;e[r>>>=0]=255&t;for(;++a>>=0;let n=e[t>>>=0],i=1,a=0;for(;++a=(i*=128)&&(n-=Math.pow(2,8*r));return n}(e,t,this.len)},put(e,t,r){return s(e,r,t,this.len)}},t.UINT64_BE={len:8,get(e,t){return o(e,t,this.len)},put(e,t,r){return c(e,r,t,this.len)}},t.INT64_BE={len:8,get(e,t){return u(e,t,this.len)},put(e,t,r){return l(e,r,t,this.len)}};t.IgnoreType=class{constructor(e){this.len=e}get(e,t){return null}};t.BufferType=class{constructor(e){this.len=e}get(e,t){return e.slice(t,t+this.len)}};t.StringType=class{constructor(e,t){this.len=e,this.encoding=t}get(e,t){return e.toString(this.encoding,t,t+this.len)}};class a{constructor(e){this.len=e}static decode(e,t,r){let n="";for(let i=t;i>10),56320+(1023&e)))}static singleByteDecoder(e){if(a.inRange(e,0,127))return e;const t=a.windows1252[e-128];if(null===t)throw Error("invaliding encoding");return t}get(e,t=0){return a.decode(e,t,t+this.len)}}function s(e,t,r,n){t=+t;let i=0,a=1,s=0;for(e[r>>>=0]=255&t;++i>0)-s&255;return r+n}function o(e,t,r){r>>>=0;let n=e[(t>>>=0)+--r],i=1;for(;r>0&&(i*=256);)n+=e[t+--r]*i;return n}function c(e,t,r,n){t=+t;let i=(n>>>=0)-1,a=1;for(e[(r>>>=0)+i]=255&t;--i>=0&&(a*=256);)e[r+i]=t/a&255;return r+n}function u(e,t,r){let n=r>>>=0,i=1,a=e[(t>>>=0)+--n];for(;n>0&&(i*=256);)a+=e[t+--n]*i;return a>=(i*=128)&&(a-=Math.pow(2,8*r)),a}function l(e,t,r,n){t=+t;let i=n-1,a=1,s=0;for(e[(r>>>=0)+i]=255&t;--i>=0&&(a*=256);)t<0&&0===s&&0!==e[r+i+1]&&(s=1),e[r+i]=(t/a>>0)-s&255;return r+n}a.windows1252=[8364,129,8218,402,8222,8230,8224,8225,710,8240,352,8249,338,141,381,143,144,8216,8217,8220,8221,8226,8211,8212,732,8482,353,8250,339,157,382,376,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255],t.AnsiStringType=a,t.writeIntLE=s,t.readUIntBE=o,t.writeUIntBE=c,t.readIntBE=u,t.writeIntBE=l},function(e,t,r){(function(n){t.log=function(...e){return"object"==typeof console&&console.log&&console.log(...e)},t.formatArgs=function(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+e.exports.humanize(this.diff),!this.useColors)return;const r="color: "+this.color;t.splice(1,0,r,"color: inherit");let n=0,i=0;t[0].replace(/%[a-zA-Z%]/g,e=>{"%%"!==e&&(n++,"%c"===e&&(i=n))}),t.splice(i,0,r)},t.save=function(e){try{e?t.storage.setItem("debug",e):t.storage.removeItem("debug")}catch(e){}},t.load=function(){let e;try{e=t.storage.getItem("debug")}catch(e){}!e&&void 0!==n&&"env"in n&&(e=n.env.DEBUG);return e},t.useColors=function(){if("undefined"!=typeof window&&window.process&&("renderer"===window.process.type||window.process.__nwjs))return!0;if("undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))return!1;return"undefined"!=typeof document&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||"undefined"!=typeof window&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)},t.storage=function(){try{return localStorage}catch(e){}}(),t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"],e.exports=r(209)(t);const{formatters:i}=e.exports;i.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}}).call(this,r(9))},function(e,t,r){"use strict";(function(e){var n=r(187),i=r(188),a=r(34);function s(){return c.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function o(e,t){if(s()=s())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+s().toString(16)+" bytes");return 0|e}function h(e,t){if(c.isBuffer(e))return e.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(e)||e instanceof ArrayBuffer))return e.byteLength;"string"!=typeof e&&(e=""+e);var r=e.length;if(0===r)return 0;for(var n=!1;;)switch(t){case"ascii":case"latin1":case"binary":return r;case"utf8":case"utf-8":case void 0:return j(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*r;case"hex":return r>>>1;case"base64":return q(e).length;default:if(n)return j(e).length;t=(""+t).toLowerCase(),n=!0}}function m(e,t,r){var n=!1;if((void 0===t||t<0)&&(t=0),t>this.length)return"";if((void 0===r||r>this.length)&&(r=this.length),r<=0)return"";if((r>>>=0)<=(t>>>=0))return"";for(e||(e="utf8");;)switch(e){case"hex":return A(this,t,r);case"utf8":case"utf-8":return E(this,t,r);case"ascii":return F(this,t,r);case"latin1":case"binary":return x(this,t,r);case"base64":return C(this,t,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return P(this,t,r);default:if(n)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),n=!0}}function g(e,t,r){var n=e[t];e[t]=e[r],e[r]=n}function b(e,t,r,n,i){if(0===e.length)return-1;if("string"==typeof r?(n=r,r=0):r>2147483647?r=2147483647:r<-2147483648&&(r=-2147483648),r=+r,isNaN(r)&&(r=i?0:e.length-1),r<0&&(r=e.length+r),r>=e.length){if(i)return-1;r=e.length-1}else if(r<0){if(!i)return-1;r=0}if("string"==typeof t&&(t=c.from(t,n)),c.isBuffer(t))return 0===t.length?-1:y(e,t,r,n,i);if("number"==typeof t)return t&=255,c.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?i?Uint8Array.prototype.indexOf.call(e,t,r):Uint8Array.prototype.lastIndexOf.call(e,t,r):y(e,[t],r,n,i);throw new TypeError("val must be string, number or Buffer")}function y(e,t,r,n,i){var a,s=1,o=e.length,c=t.length;if(void 0!==n&&("ucs2"===(n=String(n).toLowerCase())||"ucs-2"===n||"utf16le"===n||"utf-16le"===n)){if(e.length<2||t.length<2)return-1;s=2,o/=2,c/=2,r/=2}function u(e,t){return 1===s?e[t]:e.readUInt16BE(t*s)}if(i){var l=-1;for(a=r;ao&&(r=o-c),a=r;a>=0;a--){for(var d=!0,f=0;fi&&(n=i):n=i;var a=t.length;if(a%2!=0)throw new TypeError("Invalid hex string");n>a/2&&(n=a/2);for(var s=0;s>8,i=r%256,a.push(i),a.push(n);return a}(t,e.length-r),e,r,n)}function C(e,t,r){return 0===t&&r===e.length?n.fromByteArray(e):n.fromByteArray(e.slice(t,r))}function E(e,t,r){r=Math.min(e.length,r);for(var n=[],i=t;i239?4:u>223?3:u>191?2:1;if(i+d<=r)switch(d){case 1:u<128&&(l=u);break;case 2:128==(192&(a=e[i+1]))&&(c=(31&u)<<6|63&a)>127&&(l=c);break;case 3:a=e[i+1],s=e[i+2],128==(192&a)&&128==(192&s)&&(c=(15&u)<<12|(63&a)<<6|63&s)>2047&&(c<55296||c>57343)&&(l=c);break;case 4:a=e[i+1],s=e[i+2],o=e[i+3],128==(192&a)&&128==(192&s)&&128==(192&o)&&(c=(15&u)<<18|(63&a)<<12|(63&s)<<6|63&o)>65535&&c<1114112&&(l=c)}null===l?(l=65533,d=1):l>65535&&(l-=65536,n.push(l>>>10&1023|55296),l=56320|1023&l),n.push(l),i+=d}return function(e){var t=e.length;if(t<=_)return String.fromCharCode.apply(String,e);var r="",n=0;for(;n0&&(e=this.toString("hex",0,r).match(/.{2}/g).join(" "),this.length>r&&(e+=" ... ")),""},c.prototype.compare=function(e,t,r,n,i){if(!c.isBuffer(e))throw new TypeError("Argument must be a Buffer");if(void 0===t&&(t=0),void 0===r&&(r=e?e.length:0),void 0===n&&(n=0),void 0===i&&(i=this.length),t<0||r>e.length||n<0||i>this.length)throw new RangeError("out of range index");if(n>=i&&t>=r)return 0;if(n>=i)return-1;if(t>=r)return 1;if(this===e)return 0;for(var a=(i>>>=0)-(n>>>=0),s=(r>>>=0)-(t>>>=0),o=Math.min(a,s),u=this.slice(n,i),l=e.slice(t,r),d=0;di)&&(r=i),e.length>0&&(r<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");n||(n="utf8");for(var a=!1;;)switch(n){case"hex":return w(this,e,t,r);case"utf8":case"utf-8":return T(this,e,t,r);case"ascii":return v(this,e,t,r);case"latin1":case"binary":return I(this,e,t,r);case"base64":return k(this,e,t,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return S(this,e,t,r);default:if(a)throw new TypeError("Unknown encoding: "+n);n=(""+n).toLowerCase(),a=!0}},c.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var _=4096;function F(e,t,r){var n="";r=Math.min(e.length,r);for(var i=t;in)&&(r=n);for(var i="",a=t;ar)throw new RangeError("Trying to access beyond buffer length")}function M(e,t,r,n,i,a){if(!c.isBuffer(e))throw new TypeError('"buffer" argument must be a Buffer instance');if(t>i||te.length)throw new RangeError("Index out of range")}function D(e,t,r,n){t<0&&(t=65535+t+1);for(var i=0,a=Math.min(e.length-r,2);i>>8*(n?i:1-i)}function O(e,t,r,n){t<0&&(t=4294967295+t+1);for(var i=0,a=Math.min(e.length-r,4);i>>8*(n?i:3-i)&255}function R(e,t,r,n,i,a){if(r+n>e.length)throw new RangeError("Index out of range");if(r<0)throw new RangeError("Index out of range")}function U(e,t,r,n,a){return a||R(e,0,r,4),i.write(e,t,r,n,23,4),r+4}function L(e,t,r,n,a){return a||R(e,0,r,8),i.write(e,t,r,n,52,8),r+8}c.prototype.slice=function(e,t){var r,n=this.length;if((e=~~e)<0?(e+=n)<0&&(e=0):e>n&&(e=n),(t=void 0===t?n:~~t)<0?(t+=n)<0&&(t=0):t>n&&(t=n),t0&&(i*=256);)n+=this[e+--t]*i;return n},c.prototype.readUInt8=function(e,t){return t||B(e,1,this.length),this[e]},c.prototype.readUInt16LE=function(e,t){return t||B(e,2,this.length),this[e]|this[e+1]<<8},c.prototype.readUInt16BE=function(e,t){return t||B(e,2,this.length),this[e]<<8|this[e+1]},c.prototype.readUInt32LE=function(e,t){return t||B(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},c.prototype.readUInt32BE=function(e,t){return t||B(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},c.prototype.readIntLE=function(e,t,r){e|=0,t|=0,r||B(e,t,this.length);for(var n=this[e],i=1,a=0;++a=(i*=128)&&(n-=Math.pow(2,8*t)),n},c.prototype.readIntBE=function(e,t,r){e|=0,t|=0,r||B(e,t,this.length);for(var n=t,i=1,a=this[e+--n];n>0&&(i*=256);)a+=this[e+--n]*i;return a>=(i*=128)&&(a-=Math.pow(2,8*t)),a},c.prototype.readInt8=function(e,t){return t||B(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},c.prototype.readInt16LE=function(e,t){t||B(e,2,this.length);var r=this[e]|this[e+1]<<8;return 32768&r?4294901760|r:r},c.prototype.readInt16BE=function(e,t){t||B(e,2,this.length);var r=this[e+1]|this[e]<<8;return 32768&r?4294901760|r:r},c.prototype.readInt32LE=function(e,t){return t||B(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},c.prototype.readInt32BE=function(e,t){return t||B(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},c.prototype.readFloatLE=function(e,t){return t||B(e,4,this.length),i.read(this,e,!0,23,4)},c.prototype.readFloatBE=function(e,t){return t||B(e,4,this.length),i.read(this,e,!1,23,4)},c.prototype.readDoubleLE=function(e,t){return t||B(e,8,this.length),i.read(this,e,!0,52,8)},c.prototype.readDoubleBE=function(e,t){return t||B(e,8,this.length),i.read(this,e,!1,52,8)},c.prototype.writeUIntLE=function(e,t,r,n){(e=+e,t|=0,r|=0,n)||M(this,e,t,r,Math.pow(2,8*r)-1,0);var i=1,a=0;for(this[t]=255&e;++a=0&&(a*=256);)this[t+i]=e/a&255;return t+r},c.prototype.writeUInt8=function(e,t,r){return e=+e,t|=0,r||M(this,e,t,1,255,0),c.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),this[t]=255&e,t+1},c.prototype.writeUInt16LE=function(e,t,r){return e=+e,t|=0,r||M(this,e,t,2,65535,0),c.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):D(this,e,t,!0),t+2},c.prototype.writeUInt16BE=function(e,t,r){return e=+e,t|=0,r||M(this,e,t,2,65535,0),c.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):D(this,e,t,!1),t+2},c.prototype.writeUInt32LE=function(e,t,r){return e=+e,t|=0,r||M(this,e,t,4,4294967295,0),c.TYPED_ARRAY_SUPPORT?(this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e):O(this,e,t,!0),t+4},c.prototype.writeUInt32BE=function(e,t,r){return e=+e,t|=0,r||M(this,e,t,4,4294967295,0),c.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):O(this,e,t,!1),t+4},c.prototype.writeIntLE=function(e,t,r,n){if(e=+e,t|=0,!n){var i=Math.pow(2,8*r-1);M(this,e,t,r,i-1,-i)}var a=0,s=1,o=0;for(this[t]=255&e;++a>0)-o&255;return t+r},c.prototype.writeIntBE=function(e,t,r,n){if(e=+e,t|=0,!n){var i=Math.pow(2,8*r-1);M(this,e,t,r,i-1,-i)}var a=r-1,s=1,o=0;for(this[t+a]=255&e;--a>=0&&(s*=256);)e<0&&0===o&&0!==this[t+a+1]&&(o=1),this[t+a]=(e/s>>0)-o&255;return t+r},c.prototype.writeInt8=function(e,t,r){return e=+e,t|=0,r||M(this,e,t,1,127,-128),c.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),e<0&&(e=255+e+1),this[t]=255&e,t+1},c.prototype.writeInt16LE=function(e,t,r){return e=+e,t|=0,r||M(this,e,t,2,32767,-32768),c.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):D(this,e,t,!0),t+2},c.prototype.writeInt16BE=function(e,t,r){return e=+e,t|=0,r||M(this,e,t,2,32767,-32768),c.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):D(this,e,t,!1),t+2},c.prototype.writeInt32LE=function(e,t,r){return e=+e,t|=0,r||M(this,e,t,4,2147483647,-2147483648),c.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24):O(this,e,t,!0),t+4},c.prototype.writeInt32BE=function(e,t,r){return e=+e,t|=0,r||M(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),c.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):O(this,e,t,!1),t+4},c.prototype.writeFloatLE=function(e,t,r){return U(this,e,t,!0,r)},c.prototype.writeFloatBE=function(e,t,r){return U(this,e,t,!1,r)},c.prototype.writeDoubleLE=function(e,t,r){return L(this,e,t,!0,r)},c.prototype.writeDoubleBE=function(e,t,r){return L(this,e,t,!1,r)},c.prototype.copy=function(e,t,r,n){if(r||(r=0),n||0===n||(n=this.length),t>=e.length&&(t=e.length),t||(t=0),n>0&&n=this.length)throw new RangeError("sourceStart out of bounds");if(n<0)throw new RangeError("sourceEnd out of bounds");n>this.length&&(n=this.length),e.length-t=0;--i)e[i+t]=this[i+r];else if(a<1e3||!c.TYPED_ARRAY_SUPPORT)for(i=0;i>>=0,r=void 0===r?this.length:r>>>0,e||(e=0),"number"==typeof e)for(a=t;a55295&&r<57344){if(!i){if(r>56319){(t-=3)>-1&&a.push(239,191,189);continue}if(s+1===n){(t-=3)>-1&&a.push(239,191,189);continue}i=r;continue}if(r<56320){(t-=3)>-1&&a.push(239,191,189),i=r;continue}r=65536+(i-55296<<10|r-56320)}else i&&(t-=3)>-1&&a.push(239,191,189);if(i=null,r<128){if((t-=1)<0)break;a.push(r)}else if(r<2048){if((t-=2)<0)break;a.push(r>>6|192,63&r|128)}else if(r<65536){if((t-=3)<0)break;a.push(r>>12|224,r>>6&63|128,63&r|128)}else{if(!(r<1114112))throw new Error("Invalid code point");if((t-=4)<0)break;a.push(r>>18|240,r>>12&63|128,r>>6&63|128,63&r|128)}}return a}function q(e){return n.toByteArray(function(e){if((e=function(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}(e).replace(z,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function H(e,t,r,n){for(var i=0;i=t.length||i>=e.length);++i)t[i+r]=e[i];return i}}).call(this,r(12))},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(6),i=r(215);class a{static findZero(e,t,r,n){let i=t;if("utf16"===n){for(;0!==e[i]||0!==e[i+1];){if(i>=r)return r;i+=2}return i}for(;0!==e[i];){if(i>=r)return r;i++}return i}static trimRightNull(e){const t=e.indexOf("\0");return-1===t?e:e.substr(0,t)}static swapBytes(e){const t=e.length;n.ok(0==(1&t),"Buffer length must be even");for(let r=0;r>i;const o=8-i,c=n-o;return c<0?s>>=8-i-n:c>0&&(s<<=c,s|=a.getBitAllignedNumber(e,t,r+o,c)),s}static isBitSet(e,t,r){return 1===a.getBitAllignedNumber(e,t,r,1)}static a2hex(e){const t=[];for(let r=0,n=e.length;r0!=(e[t]&1<e.trim().toLowerCase());if(t.length>=1){const e=parseFloat(t[0]);return 2===t.length&&"db"===t[1]?{dB:e,ratio:o(e)}:{dB:s(e),ratio:e}}}},function(e,t,r){"use strict";(function(e){Object.defineProperty(t,"__esModule",{value:!0});const n=r(4),i=/^[\w-©][\w-\x000-3]/;t.FourCcToken={len:4,get:(e,r)=>{const a=e.toString("binary",r,r+t.FourCcToken.len);if(!a.match(i))throw new Error(`FourCC contains invalid characters: ${n.default.a2hex(a)}`);return a},put:(t,r,n)=>{const i=e.from(n,"binary");if(4!==i.length)throw new Error("Invalid length");return i.copy(t,r)}}}).call(this,r(3).Buffer)},function(e,t,r){"use strict";(function(t){var n=r(196);function i(e,t){if(e===t)return 0;for(var r=e.length,n=t.length,i=0,a=Math.min(r,n);i=0;u--)if(l[u]!==d[u])return!1;for(u=l.length-1;u>=0;u--)if(o=l[u],!w(e[o],t[o],r,n))return!1;return!0}(e,t,r,n))}return r?e===t:e==t}function T(e){return"[object Arguments]"==Object.prototype.toString.call(e)}function v(e,t){if(!e||!t)return!1;if("[object RegExp]"==Object.prototype.toString.call(t))return t.test(e);try{if(e instanceof t)return!0}catch(e){}return!Error.isPrototypeOf(t)&&!0===t.call({},e)}function I(e,t,r,n){var i;if("function"!=typeof t)throw new TypeError('"block" argument must be a function');"string"==typeof r&&(n=r,r=null),i=function(e){var t;try{e()}catch(e){t=e}return t}(t),n=(r&&r.name?" ("+r.name+").":".")+(n?" "+n:"."),e&&!i&&b(i,r,"Missing expected exception"+n);var a="string"==typeof n,o=!e&&i&&!r;if((!e&&s.isError(i)&&a&&v(i,r)||o)&&b(i,r,"Got unwanted exception"+n),e&&i&&r&&!v(i,r)||!e&&i)throw i}f.AssertionError=function(e){this.name="AssertionError",this.actual=e.actual,this.expected=e.expected,this.operator=e.operator,e.message?(this.message=e.message,this.generatedMessage=!1):(this.message=function(e){return m(g(e.actual),128)+" "+e.operator+" "+m(g(e.expected),128)}(this),this.generatedMessage=!0);var t=e.stackStartFunction||b;if(Error.captureStackTrace)Error.captureStackTrace(this,t);else{var r=new Error;if(r.stack){var n=r.stack,i=h(t),a=n.indexOf("\n"+i);if(a>=0){var s=n.indexOf("\n",a+1);n=n.substring(s+1)}this.stack=n}}},s.inherits(f.AssertionError,Error),f.fail=b,f.ok=y,f.equal=function(e,t,r){e!=t&&b(e,t,r,"==",f.equal)},f.notEqual=function(e,t,r){e==t&&b(e,t,r,"!=",f.notEqual)},f.deepEqual=function(e,t,r){w(e,t,!1)||b(e,t,r,"deepEqual",f.deepEqual)},f.deepStrictEqual=function(e,t,r){w(e,t,!0)||b(e,t,r,"deepStrictEqual",f.deepStrictEqual)},f.notDeepEqual=function(e,t,r){w(e,t,!1)&&b(e,t,r,"notDeepEqual",f.notDeepEqual)},f.notDeepStrictEqual=function e(t,r,n){w(t,r,!0)&&b(t,r,n,"notDeepStrictEqual",e)},f.strictEqual=function(e,t,r){e!==t&&b(e,t,r,"===",f.strictEqual)},f.notStrictEqual=function(e,t,r){e===t&&b(e,t,r,"!==",f.notStrictEqual)},f.throws=function(e,t,r){I(!0,e,t,r)},f.doesNotThrow=function(e,t,r){I(!1,e,t,r)},f.ifError=function(e){if(e)throw e},f.strict=n(function e(t,r){t||b(t,!0,r,"==",e)},f,{equal:f.strictEqual,deepEqual:f.deepStrictEqual,notEqual:f.notStrictEqual,notDeepEqual:f.notDeepStrictEqual}),f.strict.strict=f.strict;var k=Object.keys||function(e){var t=[];for(var r in e)o.call(e,r)&&t.push(r);return t}}).call(this,r(12))},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.BasicParser=class{constructor(){this.warnings=[]}init(e,t,r){return this.metadata=e,this.tokenizer=t,this.options=r,this}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(25);class i{constructor(e,t){this.tagTypes=e,this.tagMap=t}static parseGenre(e){const t=e.trim().split(/\((.*?)\)/g).filter(e=>""!==e),r=[];for(let e of t)/^\d+$/.test(e)&&!isNaN(parseInt(e,10))&&(e=n.Genres[e]),r.push(e);return r.filter(e=>void 0!==e).join("/")}static fixPictureMimeType(e){switch(e=e.toLocaleLowerCase()){case"image/jpg":return"image/jpeg"}return e}static toIntOrNull(e){const t=parseInt(e,10);return isNaN(t)?null:t}static normalizeTrack(e){const t=e.toString().split("/");return{no:parseInt(t[0],10)||null,of:parseInt(t[1],10)||null}}mapGenericTag(e){e={id:e.id,value:e.value},this.postMap(e);const t=this.getCommonName(e.id);return t?{id:t,value:e.value}:null}getCommonName(e){return this.tagMap[e]}postMap(e){}}i.maxRatingScore=1,t.CommonTagMapper=i},function(e,t){var r,n,i=e.exports={};function a(){throw new Error("setTimeout has not been defined")}function s(){throw new Error("clearTimeout has not been defined")}function o(e){if(r===setTimeout)return setTimeout(e,0);if((r===a||!r)&&setTimeout)return r=setTimeout,setTimeout(e,0);try{return r(e,0)}catch(t){try{return r.call(null,e,0)}catch(t){return r.call(this,e,0)}}}!function(){try{r="function"==typeof setTimeout?setTimeout:a}catch(e){r=a}try{n="function"==typeof clearTimeout?clearTimeout:s}catch(e){n=s}}();var c,u=[],l=!1,d=-1;function f(){l&&c&&(l=!1,c.length?u=c.concat(u):d=-1,u.length&&p())}function p(){if(!l){var e=o(f);l=!0;for(var t=u.length;t;){for(c=u,u=[];++d1)for(var r=1;r0?this.parseExtendedHeaderData(t,e.size):this.parseId3Data(this.id3Header.size-e.size)}async parseExtendedHeaderData(t,r){const n=e.alloc(t);return await this.tokenizer.readBuffer(n,0,t),this.parseId3Data(this.id3Header.size-r)}async parseId3Data(t){const r=e.alloc(t);await this.tokenizer.readBuffer(r,0,t);for(const e of this.parseMetadata(r))if("TXXX"===e.id)for(const t of e.value.text)this.addTag(o.makeDescriptionTagName(e.id,e.value.description),t);else if("COM"===e.id)for(const t of e.value)this.addTag(o.makeDescriptionTagName(e.id,t.description),t.text);else if(Array.isArray(e.value))for(const t of e.value)this.addTag(e.id,t);else this.addTag(e.id,e.value)}addTag(e,t){this.metadata.addTag(this.headerType,e,t)}parseMetadata(e){let t=0;const r=[];for(;t!==e.length;){const n=o.getFrameHeaderLength(this.id3Header.version.major);if(t+n>e.length)break;const i=e.slice(t,t+=n),a=o.readFrameHeader(i,this.id3Header.version.major);if(""===a.id||"\0\0\0\0"===a.id||-1==="ABCDEFGHIJKLMNOPQRSTUVWXYZ".indexOf(a.id[0]))break;const s=e.slice(t,t+=a.length),c=o.readFrameData(s,a,this.id3Header.version.major,!this.options.skipCovers);r.push({id:a.id,value:c})}return r}}t.ID3v2Parser=o}).call(this,r(3).Buffer)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(4);!function(e){e[e.Other=0]="Other",e[e["32x32 pixels 'file icon' (PNG only)"]=1]="32x32 pixels 'file icon' (PNG only)",e[e["Other file icon"]=2]="Other file icon",e[e["Cover (front)"]=3]="Cover (front)",e[e["Cover (back)"]=4]="Cover (back)",e[e["Leaflet page"]=5]="Leaflet page",e[e["Media (e.g. label side of CD)"]=6]="Media (e.g. label side of CD)",e[e["Lead artist/lead performer/soloist"]=7]="Lead artist/lead performer/soloist",e[e["Artist/performer"]=8]="Artist/performer",e[e.Conductor=9]="Conductor",e[e["Band/Orchestra"]=10]="Band/Orchestra",e[e.Composer=11]="Composer",e[e["Lyricist/text writer"]=12]="Lyricist/text writer",e[e["Recording Location"]=13]="Recording Location",e[e["During recording"]=14]="During recording",e[e["During performance"]=15]="During performance",e[e["Movie/video screen capture"]=16]="Movie/video screen capture",e[e["A bright coloured fish"]=17]="A bright coloured fish",e[e.Illustration=18]="Illustration",e[e["Band/artist logotype"]=19]="Band/artist logotype",e[e["Publisher/Studio logotype"]=20]="Publisher/Studio logotype"}(t.AttachedPictureType||(t.AttachedPictureType={})),t.UINT32SYNCSAFE={get:(e,t)=>127&e[t+3]|e[t+2]<<7|e[t+1]<<14|e[t]<<21,len:4},t.ID3v2Header={len:10,get:(e,r)=>({fileIdentifier:new n.StringType(3,"ascii").get(e,r),version:{major:n.INT8.get(e,r+3),revision:n.INT8.get(e,r+4)},flags:{raw:n.INT8.get(e,r+4),unsynchronisation:i.default.strtokBITSET.get(e,r+5,7),isExtendedHeader:i.default.strtokBITSET.get(e,r+5,6),expIndicator:i.default.strtokBITSET.get(e,r+5,5),footer:i.default.strtokBITSET.get(e,r+5,4)},size:t.UINT32SYNCSAFE.get(e,r+6)})},t.ExtendedHeader={len:10,get:(e,t)=>({size:n.UINT32_BE.get(e,t),extendedFlags:n.UINT16_BE.get(e,t+4),sizeOfPadding:n.UINT32_BE.get(e,t+6),crcDataPresent:i.default.strtokBITSET.get(e,t+4,31)})},t.TextEncodingToken={len:1,get:(e,t)=>{switch(e.readUInt8(t)){case 0:return{encoding:"iso-8859-1"};case 1:return{encoding:"utf16",bom:!0};case 2:return{encoding:"utf16",bom:!1};case 3:default:return{encoding:"utf8",bom:!1}}}}},function(e,t,r){(function(e){function r(e){return Object.prototype.toString.call(e)}t.isArray=function(e){return Array.isArray?Array.isArray(e):"[object Array]"===r(e)},t.isBoolean=function(e){return"boolean"==typeof e},t.isNull=function(e){return null===e},t.isNullOrUndefined=function(e){return null==e},t.isNumber=function(e){return"number"==typeof e},t.isString=function(e){return"string"==typeof e},t.isSymbol=function(e){return"symbol"==typeof e},t.isUndefined=function(e){return void 0===e},t.isRegExp=function(e){return"[object RegExp]"===r(e)},t.isObject=function(e){return"object"==typeof e&&null!==e},t.isDate=function(e){return"[object Date]"===r(e)},t.isError=function(e){return"[object Error]"===r(e)||e instanceof Error},t.isFunction=function(e){return"function"==typeof e},t.isPrimitive=function(e){return null===e||"boolean"==typeof e||"number"==typeof e||"string"==typeof e||"symbol"==typeof e||void 0===e},t.isBuffer=e.isBuffer}).call(this,r(3).Buffer)},,function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(193),i=r(203);t.fromStream=function(e,t){return new n.ReadStreamTokenizer(e,t)},t.fromBuffer=function(e){return new i.BufferTokenizer(e)}},function(e,t,r){"use strict";(function(t){void 0===t||!t.version||0===t.version.indexOf("v0.")||0===t.version.indexOf("v1.")&&0!==t.version.indexOf("v1.8.")?e.exports={nextTick:function(e,r,n,i){if("function"!=typeof e)throw new TypeError('"callback" argument must be a function');var a,s,o=arguments.length;switch(o){case 0:case 1:return t.nextTick(e);case 2:return t.nextTick(function(){e.call(null,r)});case 3:return t.nextTick(function(){e.call(null,r,n)});case 4:return t.nextTick(function(){e.call(null,r,n,i)});default:for(a=new Array(o-1),s=0;s1?e.blocksPerFrame*(e.totalFrames-1):0;return(t+=e.finalFrameBlocks)/e.sampleRate}static async parseTagHeader(t,r,n){if(r.fileSize&&r.fileSize-r.position0?this.parseDescriptorExpansion(t):this.parseHeader());return await this.tokenizer.readToken(new a.IgnoreType(r.forwardBytes)),p.parseTagHeader(this.metadata,this.tokenizer,this.options)}async parseDescriptorExpansion(e){return await this.tokenizer.readToken(new a.IgnoreType(e)),this.parseHeader()}async parseHeader(){const e=await this.tokenizer.readToken(u.Header);return this.metadata.setFormat("lossless",!0),this.metadata.setFormat("container","Monkey's Audio"),this.metadata.setFormat("bitsPerSample",e.bitsPerSample),this.metadata.setFormat("sampleRate",e.sampleRate),this.metadata.setFormat("numberOfChannels",e.channel),this.metadata.setFormat("duration",p.calculateDuration(e)),{forwardBytes:this.ape.descriptor.seekTableBytes+this.ape.descriptor.headerDataBytes+this.ape.descriptor.apeFrameDataBytes+this.ape.descriptor.terminatingDataBytes}}}t.APEv2Parser=p}).call(this,r(3).Buffer)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(10),i=r(18),a=r(17),s=r(25),o=r(2),c=r(7),u=o("music-metadata:parser:ID3");t.AbstractID3Parser=class extends c.BasicParser{constructor(){super(...arguments),this.id3parser=new a.ID3v2Parser}static async startsWithID3v2Header(e){return"ID3"===(await e.peekToken(i.ID3v2Header)).fileIdentifier}parse(){return this.parseID3v2().catch(e=>{if(e.message!==n.endOfFile)throw e})}finalize(){}async parseID3v2(){if(await this.tryReadId3v2Headers(),u("End of ID3v2 header, go to MPEG-parser: pos=%s",this.tokenizer.position),await this._parse(),this.options.skipPostHeaders&&this.metadata.hasAny())this.finalize();else{const e=new s.ID3v1Parser;await e.init(this.metadata,this.tokenizer,this.options).parse(),this.finalize()}}async tryReadId3v2Headers(){if("ID3"===(await this.tokenizer.peekToken(i.ID3v2Header)).fileIdentifier)return u("Found ID3v2 header, pos=%s",this.tokenizer.position),await this.id3parser.parse(this.metadata,this.tokenizer,this.options),this.tryReadId3v2Headers()}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(2),i=r(4),a=r(1),s=r(7),o=n("music-metadata:parser:ID3v1");t.Genres=["Blues","Classic Rock","Country","Dance","Disco","Funk","Grunge","Hip-Hop","Jazz","Metal","New Age","Oldies","Other","Pop","R&B","Rap","Reggae","Rock","Techno","Industrial","Alternative","Ska","Death Metal","Pranks","Soundtrack","Euro-Techno","Ambient","Trip-Hop","Vocal","Jazz+Funk","Fusion","Trance","Classical","Instrumental","Acid","House","Game","Sound Clip","Gospel","Noise","Alt. Rock","Bass","Soul","Punk","Space","Meditative","Instrumental Pop","Instrumental Rock","Ethnic","Gothic","Darkwave","Techno-Industrial","Electronic","Pop-Folk","Eurodance","Dream","Southern Rock","Comedy","Cult","Gangsta Rap","Top 40","Christian Rap","Pop/Funk","Jungle","Native American","Cabaret","New Wave","Psychedelic","Rave","Showtunes","Trailer","Lo-Fi","Tribal","Acid Punk","Acid Jazz","Polka","Retro","Musical","Rock & Roll","Hard Rock","Folk","Folk/Rock","National Folk","Swing","Fast-Fusion","Bebob","Latin","Revival","Celtic","Bluegrass","Avantgarde","Gothic Rock","Progressive Rock","Psychedelic Rock","Symphonic Rock","Slow Rock","Big Band","Chorus","Easy Listening","Acoustic","Humour","Speech","Chanson","Opera","Chamber Music","Sonata","Symphony","Booty Bass","Primus","Porn Groove","Satire","Slow Jam","Club","Tango","Samba","Folklore","Ballad","Power Ballad","Rhythmic Soul","Freestyle","Duet","Punk Rock","Drum Solo","A Cappella","Euro-House","Dance Hall","Goa","Drum & Bass","Club-House","Hardcore","Terror","Indie","BritPop","Negerpunk","Polsk Punk","Beat","Christian Gangsta Rap","Heavy Metal","Black Metal","Crossover","Contemporary Christian","Christian Rock","Merengue","Salsa","Thrash Metal","Anime","JPop","Synthpop","Abstract","Art Rock","Baroque","Bhangra","Big Beat","Breakbeat","Chillout","Downtempo","Dub","EBM","Eclectic","Electro","Electroclash","Emo","Experimental","Garage","Global","IDM","Illbient","Industro-Goth","Jam Band","Krautrock","Leftfield","Lounge","Math Rock","New Romantic","Nu-Breakz","Post-Punk","Post-Rock","Psytrance","Shoegaze","Space Rock","Trop Rock","World Music","Neoclassical","Audiobook","Audio Theatre","Neue Deutsche Welle","Podcast","Indie Rock","G-Funk","Dubstep","Garage Rock","Psybient"];const c={len:128,get:(e,t)=>{const r=new u(3).get(e,t);return"TAG"===r?{header:r,title:new u(30).get(e,t+3),artist:new u(30).get(e,t+33),album:new u(30).get(e,t+63),year:new u(4).get(e,t+93),comment:new u(28).get(e,t+97),zeroByte:a.UINT8.get(e,t+127),track:a.UINT8.get(e,t+126),genre:a.UINT8.get(e,t+127)}:null}};class u extends a.StringType{constructor(e){super(e,"binary")}get(e,t){let r=super.get(e,t);return(r=(r=i.default.trimRightNull(r)).trim()).length>0?r:void 0}}class l extends s.BasicParser{static getGenre(e){if(ee)return void o("Already consumed the last 128 bytes");const t=await this.tokenizer.readToken(c,e);if(t){o("ID3v1 header found at: pos=%s",this.tokenizer.fileSize-c.len);for(const e of["title","artist","album","comment","track","year"])t[e]&&""!==t[e]&&this.addTag(e,t[e]);const e=l.getGenre(t.genre);e&&this.addTag("genre",e)}else o("ID3v1 header not found at: pos=%s",this.tokenizer.fileSize-c.len)}addTag(e,t){this.metadata.addTag("ID3v1",e,t)}}t.ID3v1Parser=l},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(36);t.ID3Stream=class extends n.Readable{constructor(e){super(),this.buf=e}_read(){this.push(this.buf),this.push(null)}}},function(e,t,r){"use strict";var n,i="object"==typeof Reflect?Reflect:null,a=i&&"function"==typeof i.apply?i.apply:function(e,t,r){return Function.prototype.apply.call(e,t,r)};n=i&&"function"==typeof i.ownKeys?i.ownKeys:Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:function(e){return Object.getOwnPropertyNames(e)};var s=Number.isNaN||function(e){return e!=e};function o(){o.init.call(this)}e.exports=o,o.EventEmitter=o,o.prototype._events=void 0,o.prototype._eventsCount=0,o.prototype._maxListeners=void 0;var c=10;function u(e){return void 0===e._maxListeners?o.defaultMaxListeners:e._maxListeners}function l(e,t,r,n){var i,a,s,o;if("function"!=typeof r)throw new TypeError('The "listener" argument must be of type Function. Received type '+typeof r);if(void 0===(a=e._events)?(a=e._events=Object.create(null),e._eventsCount=0):(void 0!==a.newListener&&(e.emit("newListener",t,r.listener?r.listener:r),a=e._events),s=a[t]),void 0===s)s=a[t]=r,++e._eventsCount;else if("function"==typeof s?s=a[t]=n?[r,s]:[s,r]:n?s.unshift(r):s.push(r),(i=u(e))>0&&s.length>i&&!s.warned){s.warned=!0;var c=new Error("Possible EventEmitter memory leak detected. "+s.length+" "+String(t)+" listeners added. Use emitter.setMaxListeners() to increase limit");c.name="MaxListenersExceededWarning",c.emitter=e,c.type=t,c.count=s.length,o=c,console&&console.warn&&console.warn(o)}return e}function d(e,t,r){var n={fired:!1,wrapFn:void 0,target:e,type:t,listener:r},i=function(){for(var e=[],t=0;t0&&(s=t[0]),s instanceof Error)throw s;var o=new Error("Unhandled error."+(s?" ("+s.message+")":""));throw o.context=s,o}var c=i[e];if(void 0===c)return!1;if("function"==typeof c)a(c,this,t);else{var u=c.length,l=h(c,u);for(r=0;r=0;a--)if(r[a]===t||r[a].listener===t){s=r[a].listener,i=a;break}if(i<0)return this;0===i?r.shift():function(e,t){for(;t+1=0;n--)this.removeListener(e,t[n]);return this},o.prototype.listeners=function(e){return f(this,e,!0)},o.prototype.rawListeners=function(e){return f(this,e,!1)},o.listenerCount=function(e,t){return"function"==typeof e.listenerCount?e.listenerCount(t):p.call(e,t)},o.prototype.listenerCount=p,o.prototype.eventNames=function(){return this._eventsCount>0?n(this._events):[]}},function(e,t,r){(t=e.exports=r(37)).Stream=t,t.Readable=t,t.Writable=r(30),t.Duplex=r(13),t.Transform=r(41),t.PassThrough=r(234)},function(e,t,r){var n=r(3),i=n.Buffer;function a(e,t){for(var r in e)t[r]=e[r]}function s(e,t,r){return i(e,t,r)}i.from&&i.alloc&&i.allocUnsafe&&i.allocUnsafeSlow?e.exports=n:(a(n,t),t.Buffer=s),a(i,s),s.from=function(e,t,r){if("number"==typeof e)throw new TypeError("Argument must not be a number");return i(e,t,r)},s.alloc=function(e,t,r){if("number"!=typeof e)throw new TypeError("Argument must be a number");var n=i(e);return void 0!==t?"string"==typeof r?n.fill(t,r):n.fill(t):n.fill(0),n},s.allocUnsafe=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return i(e)},s.allocUnsafeSlow=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return n.SlowBuffer(e)}},function(e,t,r){"use strict";(function(t,n,i){var a=r(22);function s(e){var t=this;this.next=null,this.entry=null,this.finish=function(){!function(e,t,r){var n=e.entry;e.entry=null;for(;n;){var i=n.callback;t.pendingcb--,i(r),n=n.next}t.corkedRequestsFree?t.corkedRequestsFree.next=e:t.corkedRequestsFree=e}(t,e)}}e.exports=y;var o,c=!t.browser&&["v0.10","v0.9."].indexOf(t.version.slice(0,5))>-1?n:a.nextTick;y.WritableState=b;var u=r(19);u.inherits=r(16);var l={deprecate:r(232)},d=r(38),f=r(29).Buffer,p=i.Uint8Array||function(){};var h,m=r(39);function g(){}function b(e,t){o=o||r(13),e=e||{};var n=t instanceof o;this.objectMode=!!e.objectMode,n&&(this.objectMode=this.objectMode||!!e.writableObjectMode);var i=e.highWaterMark,u=e.writableHighWaterMark,l=this.objectMode?16:16384;this.highWaterMark=i||0===i?i:n&&(u||0===u)?u:l,this.highWaterMark=Math.floor(this.highWaterMark),this.finalCalled=!1,this.needDrain=!1,this.ending=!1,this.ended=!1,this.finished=!1,this.destroyed=!1;var d=!1===e.decodeStrings;this.decodeStrings=!d,this.defaultEncoding=e.defaultEncoding||"utf8",this.length=0,this.writing=!1,this.corked=0,this.sync=!0,this.bufferProcessing=!1,this.onwrite=function(e){!function(e,t){var r=e._writableState,n=r.sync,i=r.writecb;if(function(e){e.writing=!1,e.writecb=null,e.length-=e.writelen,e.writelen=0}(r),t)!function(e,t,r,n,i){--t.pendingcb,r?(a.nextTick(i,n),a.nextTick(S,e,t),e._writableState.errorEmitted=!0,e.emit("error",n)):(i(n),e._writableState.errorEmitted=!0,e.emit("error",n),S(e,t))}(e,r,n,t,i);else{var s=I(r);s||r.corked||r.bufferProcessing||!r.bufferedRequest||v(e,r),n?c(T,e,r,s,i):T(e,r,s,i)}}(t,e)},this.writecb=null,this.writelen=0,this.bufferedRequest=null,this.lastBufferedRequest=null,this.pendingcb=0,this.prefinished=!1,this.errorEmitted=!1,this.bufferedRequestCount=0,this.corkedRequestsFree=new s(this)}function y(e){if(o=o||r(13),!(h.call(y,this)||this instanceof o))return new y(e);this._writableState=new b(e,this),this.writable=!0,e&&("function"==typeof e.write&&(this._write=e.write),"function"==typeof e.writev&&(this._writev=e.writev),"function"==typeof e.destroy&&(this._destroy=e.destroy),"function"==typeof e.final&&(this._final=e.final)),d.call(this)}function w(e,t,r,n,i,a,s){t.writelen=n,t.writecb=s,t.writing=!0,t.sync=!0,r?e._writev(i,t.onwrite):e._write(i,a,t.onwrite),t.sync=!1}function T(e,t,r,n){r||function(e,t){0===t.length&&t.needDrain&&(t.needDrain=!1,e.emit("drain"))}(e,t),t.pendingcb--,n(),S(e,t)}function v(e,t){t.bufferProcessing=!0;var r=t.bufferedRequest;if(e._writev&&r&&r.next){var n=t.bufferedRequestCount,i=new Array(n),a=t.corkedRequestsFree;a.entry=r;for(var o=0,c=!0;r;)i[o]=r,r.isBuf||(c=!1),r=r.next,o+=1;i.allBuffers=c,w(e,t,!0,t.length,i,"",a.finish),t.pendingcb++,t.lastBufferedRequest=null,a.next?(t.corkedRequestsFree=a.next,a.next=null):t.corkedRequestsFree=new s(t),t.bufferedRequestCount=0}else{for(;r;){var u=r.chunk,l=r.encoding,d=r.callback;if(w(e,t,!1,t.objectMode?1:u.length,u,l,d),r=r.next,t.bufferedRequestCount--,t.writing)break}null===r&&(t.lastBufferedRequest=null)}t.bufferedRequest=r,t.bufferProcessing=!1}function I(e){return e.ending&&0===e.length&&null===e.bufferedRequest&&!e.finished&&!e.writing}function k(e,t){e._final(function(r){t.pendingcb--,r&&e.emit("error",r),t.prefinished=!0,e.emit("prefinish"),S(e,t)})}function S(e,t){var r=I(t);return r&&(!function(e,t){t.prefinished||t.finalCalled||("function"==typeof e._final?(t.pendingcb++,t.finalCalled=!0,a.nextTick(k,e,t)):(t.prefinished=!0,e.emit("prefinish")))}(e,t),0===t.pendingcb&&(t.finished=!0,e.emit("finish"))),r}u.inherits(y,d),b.prototype.getBuffer=function(){for(var e=this.bufferedRequest,t=[];e;)t.push(e),e=e.next;return t},function(){try{Object.defineProperty(b.prototype,"buffer",{get:l.deprecate(function(){return this.getBuffer()},"_writableState.buffer is deprecated. Use _writableState.getBuffer instead.","DEP0003")})}catch(e){}}(),"function"==typeof Symbol&&Symbol.hasInstance&&"function"==typeof Function.prototype[Symbol.hasInstance]?(h=Function.prototype[Symbol.hasInstance],Object.defineProperty(y,Symbol.hasInstance,{value:function(e){return!!h.call(this,e)||this===y&&(e&&e._writableState instanceof b)}})):h=function(e){return e instanceof this},y.prototype.pipe=function(){this.emit("error",new Error("Cannot pipe, not readable"))},y.prototype.write=function(e,t,r){var n,i=this._writableState,s=!1,o=!i.objectMode&&(n=e,f.isBuffer(n)||n instanceof p);return o&&!f.isBuffer(e)&&(e=function(e){return f.from(e)}(e)),"function"==typeof t&&(r=t,t=null),o?t="buffer":t||(t=i.defaultEncoding),"function"!=typeof r&&(r=g),i.ended?function(e,t){var r=new Error("write after end");e.emit("error",r),a.nextTick(t,r)}(this,r):(o||function(e,t,r,n){var i=!0,s=!1;return null===r?s=new TypeError("May not write null values to stream"):"string"==typeof r||void 0===r||t.objectMode||(s=new TypeError("Invalid non-string/buffer chunk")),s&&(e.emit("error",s),a.nextTick(n,s),i=!1),i}(this,i,e,r))&&(i.pendingcb++,s=function(e,t,r,n,i,a){if(!r){var s=function(e,t,r){e.objectMode||!1===e.decodeStrings||"string"!=typeof t||(t=f.from(t,r));return t}(t,n,i);n!==s&&(r=!0,i="buffer",n=s)}var o=t.objectMode?1:n.length;t.length+=o;var c=t.length-1))throw new TypeError("Unknown encoding: "+e);return this._writableState.defaultEncoding=e,this},Object.defineProperty(y.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}}),y.prototype._write=function(e,t,r){r(new Error("_write() is not implemented"))},y.prototype._writev=null,y.prototype.end=function(e,t,r){var n=this._writableState;"function"==typeof e?(r=e,e=null,t=null):"function"==typeof t&&(r=t,t=null),null!=e&&this.write(e,t),n.corked&&(n.corked=1,this.uncork()),n.ending||n.finished||function(e,t,r){t.ending=!0,S(e,t),r&&(t.finished?a.nextTick(r):e.once("finish",r));t.ended=!0,e.writable=!1}(this,n,r)},Object.defineProperty(y.prototype,"destroyed",{get:function(){return void 0!==this._writableState&&this._writableState.destroyed},set:function(e){this._writableState&&(this._writableState.destroyed=e)}}),y.prototype.destroy=m.destroy,y.prototype._undestroy=m.undestroy,y.prototype._destroy=function(e,t){this.end(),t(e)}}).call(this,r(9),r(230).setImmediate,r(12))},function(e,t,r){"use strict";(function(e){Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(2),a=r(43),s=i("music-metadata:parser:ogg:vorbis1");t.VorbisParser=class{constructor(e,t){this.metadata=e,this.options=t,this.codecName="Vorbis I",this.pageSegments=[]}parsePage(t,r){if(t.headerType.firstPage)this.parseFirstPage(t,r);else{if(t.headerType.continued){if(0===this.pageSegments.length)throw new Error("Cannot continue on previous page");this.pageSegments.push(r)}if(t.headerType.lastPage||!t.headerType.continued){if(this.pageSegments.length>0){const t=e.concat(this.pageSegments);this.parseFullPage(t)}this.pageSegments=t.headerType.lastPage?[]:[r]}}t.headerType.lastPage&&this.calculateDuration(t)}flush(){this.parseFullPage(e.concat(this.pageSegments))}parseFirstPage(e,t){this.metadata.setFormat("codec","Vorbis I"),s("Parse first page");const r=a.CommonHeader.get(t,0);if("vorbis"!==r.vorbis)throw new Error("Metadata does not look like Vorbis");if(1!==r.packetType)throw new Error("First Ogg page should be type 1: the identification header");{const e=a.IdentificationHeader.get(t,a.CommonHeader.len);this.metadata.setFormat("sampleRate",e.sampleRate),this.metadata.setFormat("bitrate",e.bitrateNominal),this.metadata.setFormat("numberOfChannels",e.channelMode),s("sample-rate=%s[hz], bitrate=%s[b/s], channel-mode=%s",e.sampleRate,e.bitrateNominal,e.channelMode)}}parseFullPage(e){const t=a.CommonHeader.get(e,0);switch(s("Parse full page: type=%s, byteLength=%s",t.packetType,e.byteLength),t.packetType){case 3:return this.parseUserCommentList(e,a.CommonHeader.len)}}calculateDuration(e){this.metadata.format.sampleRate&&e.absoluteGranulePosition>=0&&(this.metadata.setFormat("numberOfSamples",e.absoluteGranulePosition),this.metadata.setFormat("duration",this.metadata.format.numberOfSamples/this.metadata.format.sampleRate))}parseUserCommentList(e,t){const r=n.UINT32_LE.get(e,t);t+=4,new n.StringType(r,"utf-8").get(e,t),t+=r;let i=n.UINT32_LE.get(e,t);for(t+=4;i-- >0;)t+=this.parseUserComment(e,t)}parseUserComment(e,t){const r=n.UINT32_LE.get(e,t),i=new n.StringType(r,"utf-8").get(e,t+4),o=i.indexOf("="),c=i.slice(0,o).toUpperCase();let u=i.slice(o+1);return"METADATA_BLOCK_PICTURE"===c&&(u=this.options.skipCovers?null:a.VorbisPictureToken.fromBase64(u)),null!==u&&(s("Push tag: id=%s, value=%s",c,u),this.metadata.addTag("vorbis",c,u)),n.UINT32_LE.len+r}}}).call(this,r(3).Buffer)},,,function(e,t){var r={}.toString;e.exports=Array.isArray||function(e){return"[object Array]"==r.call(e)}},function(module,exports,__webpack_require__){"use strict";(function(Buffer){const{multiByteIndexOf:multiByteIndexOf,stringToBytes:stringToBytes,readUInt64LE:readUInt64LE,tarHeaderChecksumMatches:tarHeaderChecksumMatches,uint8ArrayUtf8ByteString:uint8ArrayUtf8ByteString}=__webpack_require__(205),supported=__webpack_require__(206),xpiZipFilename=stringToBytes("META-INF/mozilla.rsa"),oxmlContentTypes=stringToBytes("[Content_Types].xml"),oxmlRels=stringToBytes("_rels/.rels"),fileType=e=>{if(!(e instanceof Uint8Array||e instanceof ArrayBuffer||Buffer.isBuffer(e)))throw new TypeError(`Expected the \`input\` argument to be of type \`Uint8Array\` or \`Buffer\` or \`ArrayBuffer\`, got \`${typeof e}\``);const t=e instanceof Uint8Array?e:new Uint8Array(e);if(!(t&&t.length>1))return;const r=(e,r)=>{r={offset:0,...r};for(let n=0;nr(stringToBytes(e),t);if(r([255,216,255]))return{ext:"jpg",mime:"image/jpeg"};if(r([137,80,78,71,13,10,26,10])){const e=33,r=t.findIndex((r,n)=>n>=e&&73===t[n]&&68===t[n+1]&&65===t[n+2]&&84===t[n+3]),n=t.subarray(e,r);return n.findIndex((e,t)=>97===n[t]&&99===n[t+1]&&84===n[t+2]&&76===n[t+3])>=0?{ext:"apng",mime:"image/apng"}:{ext:"png",mime:"image/png"}}if(r([71,73,70]))return{ext:"gif",mime:"image/gif"};if(r([87,69,66,80],{offset:8}))return{ext:"webp",mime:"image/webp"};if(r([70,76,73,70]))return{ext:"flif",mime:"image/flif"};if((r([73,73,42,0])||r([77,77,0,42]))&&r([67,82],{offset:8}))return{ext:"cr2",mime:"image/x-canon-cr2"};if(r([73,73,82,79,8,0,0,0,24]))return{ext:"orf",mime:"image/x-olympus-orf"};if(r([73,73,42,0])&&(r([16,251,134,1],{offset:4})||r([8,0,0,0,19,0],{offset:4})||r([8,0,0,0,18,0],{offset:4})))return{ext:"arw",mime:"image/x-sony-arw"};if(r([73,73,42,0,8,0,0,0])&&(r([45,0,254,0],{offset:8})||r([39,0,254,0],{offset:8})))return{ext:"dng",mime:"image/x-adobe-dng"};if(r([73,73,42,0])&&r([28,0,254,0],{offset:8}))return{ext:"nef",mime:"image/x-nikon-nef"};if(r([73,73,85,0,24,0,0,0,136,231,116,216]))return{ext:"rw2",mime:"image/x-panasonic-rw2"};if(n("FUJIFILMCCD-RAW"))return{ext:"raf",mime:"image/x-fujifilm-raf"};if(r([73,73,42,0])||r([77,77,0,42]))return{ext:"tif",mime:"image/tiff"};if(r([66,77]))return{ext:"bmp",mime:"image/bmp"};if(r([73,73,188]))return{ext:"jxr",mime:"image/vnd.ms-photo"};if(r([56,66,80,83]))return{ext:"psd",mime:"image/vnd.adobe.photoshop"};const i=[80,75,3,4];if(r(i)){if(r([109,105,109,101,116,121,112,101,97,112,112,108,105,99,97,116,105,111,110,47,101,112,117,98,43,122,105,112],{offset:30}))return{ext:"epub",mime:"application/epub+zip"};if(r(xpiZipFilename,{offset:30}))return{ext:"xpi",mime:"application/x-xpinstall"};if(n("mimetypeapplication/vnd.oasis.opendocument.text",{offset:30}))return{ext:"odt",mime:"application/vnd.oasis.opendocument.text"};if(n("mimetypeapplication/vnd.oasis.opendocument.spreadsheet",{offset:30}))return{ext:"ods",mime:"application/vnd.oasis.opendocument.spreadsheet"};if(n("mimetypeapplication/vnd.oasis.opendocument.presentation",{offset:30}))return{ext:"odp",mime:"application/vnd.oasis.opendocument.presentation"};let e,a=0,s=!1;do{const o=a+30;if(s||(s=r(oxmlContentTypes,{offset:o})||r(oxmlRels,{offset:o})),e||(n("word/",{offset:o})?e={ext:"docx",mime:"application/vnd.openxmlformats-officedocument.wordprocessingml.document"}:n("ppt/",{offset:o})?e={ext:"pptx",mime:"application/vnd.openxmlformats-officedocument.presentationml.presentation"}:n("xl/",{offset:o})&&(e={ext:"xlsx",mime:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"})),s&&e)return e;a=multiByteIndexOf(t,i,o)}while(a>=0);if(e)return e}if(r([80,75])&&(3===t[2]||5===t[2]||7===t[2])&&(4===t[3]||6===t[3]||8===t[3]))return{ext:"zip",mime:"application/zip"};if(r([48,48,48,48,48,48],{offset:148,mask:[248,248,248,248,248,248]})&&tarHeaderChecksumMatches(t))return{ext:"tar",mime:"application/x-tar"};if(r([82,97,114,33,26,7])&&(0===t[6]||1===t[6]))return{ext:"rar",mime:"application/x-rar-compressed"};if(r([31,139,8]))return{ext:"gz",mime:"application/gzip"};if(r([66,90,104]))return{ext:"bz2",mime:"application/x-bzip2"};if(r([55,122,188,175,39,28]))return{ext:"7z",mime:"application/x-7z-compressed"};if(r([120,1]))return{ext:"dmg",mime:"application/x-apple-diskimage"};if(r([102,114,101,101],{offset:4})||r([109,100,97,116],{offset:4})||r([109,111,111,118],{offset:4})||r([119,105,100,101],{offset:4}))return{ext:"mov",mime:"video/quicktime"};if(r([102,116,121,112],{offset:4})&&0!=(96&t[8])&&0!=(96&t[9])&&0!=(96&t[10])&&0!=(96&t[11])){const e=uint8ArrayUtf8ByteString(t,8,12);switch(e){case"mif1":return{ext:"heic",mime:"image/heif"};case"msf1":return{ext:"heic",mime:"image/heif-sequence"};case"heic":case"heix":return{ext:"heic",mime:"image/heic"};case"hevc":case"hevx":return{ext:"heic",mime:"image/heic-sequence"};case"qt ":return{ext:"mov",mime:"video/quicktime"};case"M4V ":case"M4VH":case"M4VP":return{ext:"m4v",mime:"video/x-m4v"};case"M4P ":return{ext:"m4p",mime:"video/mp4"};case"M4B ":return{ext:"m4b",mime:"audio/mp4"};case"M4A ":return{ext:"m4a",mime:"audio/x-m4a"};case"F4V ":return{ext:"f4v",mime:"video/mp4"};case"F4P ":return{ext:"f4p",mime:"video/mp4"};case"F4A ":return{ext:"f4a",mime:"audio/mp4"};case"F4B ":return{ext:"f4b",mime:"audio/mp4"};default:return e.startsWith("3g")?e.startsWith("3g2")?{ext:"3g2",mime:"video/3gpp2"}:{ext:"3gp",mime:"video/3gpp"}:{ext:"mp4",mime:"video/mp4"}}}if(r([77,84,104,100]))return{ext:"mid",mime:"audio/midi"};if(r([26,69,223,163])){const e=t.subarray(4,4100),r=e.findIndex((e,t,r)=>66===r[t]&&130===r[t+1]);if(-1!==r){const t=r+3,n=r=>[...r].every((r,n)=>e[t+n]===r.charCodeAt(0));if(n("matroska"))return{ext:"mkv",mime:"video/x-matroska"};if(n("webm"))return{ext:"webm",mime:"video/webm"}}}if(r([82,73,70,70])){if(r([65,86,73],{offset:8}))return{ext:"avi",mime:"video/vnd.avi"};if(r([87,65,86,69],{offset:8}))return{ext:"wav",mime:"audio/vnd.wave"};if(r([81,76,67,77],{offset:8}))return{ext:"qcp",mime:"audio/qcelp"}}if(r([48,38,178,117,142,102,207,17,166,217])){let e=30;do{const n=readUInt64LE(t,e+16);if(r([145,7,220,183,183,169,207,17,142,230,0,192,12,32,83,101],{offset:e})){if(r([64,158,105,248,77,91,207,17,168,253,0,128,95,92,68,43],{offset:e+24}))return{ext:"wma",mime:"audio/x-ms-wma"};if(r([192,239,25,188,77,91,207,17,168,253,0,128,95,92,68,43],{offset:e+24}))return{ext:"wmv",mime:"video/x-ms-asf"};break}e+=n}while(e+24<=t.length);return{ext:"asf",mime:"application/vnd.ms-asf"}}if(r([0,0,1,186])||r([0,0,1,179]))return{ext:"mpg",mime:"video/mpeg"};for(let e=0;e<2&&enew Promise((resolve,reject)=>{const stream=eval("require")("stream");readableStream.on("error",reject),readableStream.once("readable",()=>{const e=new stream.PassThrough,t=readableStream.read(module.exports.minimumBytes)||readableStream.read();try{e.fileType=fileType(t)}catch(e){reject(e)}readableStream.unshift(t),stream.pipeline?resolve(stream.pipeline(readableStream,e,()=>{})):resolve(readableStream.pipe(e))})}),Object.defineProperty(fileType,"extensions",{get:()=>new Set(supported.extensions)}),Object.defineProperty(fileType,"mimeTypes",{get:()=>new Set(supported.mimeTypes)})}).call(this,__webpack_require__(3).Buffer)},function(e,t,r){e.exports=i;var n=r(27).EventEmitter;function i(){n.call(this)}r(16)(i,n),i.Readable=r(28),i.Writable=r(235),i.Duplex=r(236),i.Transform=r(237),i.PassThrough=r(238),i.Stream=i,i.prototype.pipe=function(e,t){var r=this;function i(t){e.writable&&!1===e.write(t)&&r.pause&&r.pause()}function a(){r.readable&&r.resume&&r.resume()}r.on("data",i),e.on("drain",a),e._isStdio||t&&!1===t.end||(r.on("end",o),r.on("close",c));var s=!1;function o(){s||(s=!0,e.end())}function c(){s||(s=!0,"function"==typeof e.destroy&&e.destroy())}function u(e){if(l(),0===n.listenerCount(this,"error"))throw e}function l(){r.removeListener("data",i),e.removeListener("drain",a),r.removeListener("end",o),r.removeListener("close",c),r.removeListener("error",u),e.removeListener("error",u),r.removeListener("end",l),r.removeListener("close",l),e.removeListener("close",l)}return r.on("error",u),e.on("error",u),r.on("end",l),r.on("close",l),e.on("close",l),e.emit("pipe",r),e}},function(e,t,r){"use strict";(function(t,n){var i=r(22);e.exports=w;var a,s=r(34);w.ReadableState=y;r(27).EventEmitter;var o=function(e,t){return e.listeners(t).length},c=r(38),u=r(29).Buffer,l=t.Uint8Array||function(){};var d=r(19);d.inherits=r(16);var f=r(227),p=void 0;p=f&&f.debuglog?f.debuglog("stream"):function(){};var h,m=r(228),g=r(39);d.inherits(w,c);var b=["error","close","destroy","pause","resume"];function y(e,t){e=e||{};var n=t instanceof(a=a||r(13));this.objectMode=!!e.objectMode,n&&(this.objectMode=this.objectMode||!!e.readableObjectMode);var i=e.highWaterMark,s=e.readableHighWaterMark,o=this.objectMode?16:16384;this.highWaterMark=i||0===i?i:n&&(s||0===s)?s:o,this.highWaterMark=Math.floor(this.highWaterMark),this.buffer=new m,this.length=0,this.pipes=null,this.pipesCount=0,this.flowing=null,this.ended=!1,this.endEmitted=!1,this.reading=!1,this.sync=!0,this.needReadable=!1,this.emittedReadable=!1,this.readableListening=!1,this.resumeScheduled=!1,this.destroyed=!1,this.defaultEncoding=e.defaultEncoding||"utf8",this.awaitDrain=0,this.readingMore=!1,this.decoder=null,this.encoding=null,e.encoding&&(h||(h=r(40).StringDecoder),this.decoder=new h(e.encoding),this.encoding=e.encoding)}function w(e){if(a=a||r(13),!(this instanceof w))return new w(e);this._readableState=new y(e,this),this.readable=!0,e&&("function"==typeof e.read&&(this._read=e.read),"function"==typeof e.destroy&&(this._destroy=e.destroy)),c.call(this)}function T(e,t,r,n,i){var a,s=e._readableState;null===t?(s.reading=!1,function(e,t){if(t.ended)return;if(t.decoder){var r=t.decoder.end();r&&r.length&&(t.buffer.push(r),t.length+=t.objectMode?1:r.length)}t.ended=!0,S(e)}(e,s)):(i||(a=function(e,t){var r;n=t,u.isBuffer(n)||n instanceof l||"string"==typeof t||void 0===t||e.objectMode||(r=new TypeError("Invalid non-string/buffer chunk"));var n;return r}(s,t)),a?e.emit("error",a):s.objectMode||t&&t.length>0?("string"==typeof t||s.objectMode||Object.getPrototypeOf(t)===u.prototype||(t=function(e){return u.from(e)}(t)),n?s.endEmitted?e.emit("error",new Error("stream.unshift() after end event")):v(e,s,t,!0):s.ended?e.emit("error",new Error("stream.push() after EOF")):(s.reading=!1,s.decoder&&!r?(t=s.decoder.write(t),s.objectMode||0!==t.length?v(e,s,t,!1):E(e,s)):v(e,s,t,!1))):n||(s.reading=!1));return function(e){return!e.ended&&(e.needReadable||e.lengtht.highWaterMark&&(t.highWaterMark=function(e){return e>=I?e=I:(e--,e|=e>>>1,e|=e>>>2,e|=e>>>4,e|=e>>>8,e|=e>>>16,e++),e}(e)),e<=t.length?e:t.ended?t.length:(t.needReadable=!0,0))}function S(e){var t=e._readableState;t.needReadable=!1,t.emittedReadable||(p("emitReadable",t.flowing),t.emittedReadable=!0,t.sync?i.nextTick(C,e):C(e))}function C(e){p("emit readable"),e.emit("readable"),A(e)}function E(e,t){t.readingMore||(t.readingMore=!0,i.nextTick(_,e,t))}function _(e,t){for(var r=t.length;!t.reading&&!t.flowing&&!t.ended&&t.length=t.length?(r=t.decoder?t.buffer.join(""):1===t.buffer.length?t.buffer.head.data:t.buffer.concat(t.length),t.buffer.clear()):r=function(e,t,r){var n;ea.length?a.length:e;if(s===a.length?i+=a:i+=a.slice(0,e),0===(e-=s)){s===a.length?(++n,r.next?t.head=r.next:t.head=t.tail=null):(t.head=r,r.data=a.slice(s));break}++n}return t.length-=n,i}(e,t):function(e,t){var r=u.allocUnsafe(e),n=t.head,i=1;n.data.copy(r),e-=n.data.length;for(;n=n.next;){var a=n.data,s=e>a.length?a.length:e;if(a.copy(r,r.length-e,0,s),0===(e-=s)){s===a.length?(++i,n.next?t.head=n.next:t.head=t.tail=null):(t.head=n,n.data=a.slice(s));break}++i}return t.length-=i,r}(e,t);return n}(e,t.buffer,t.decoder),r);var r}function B(e){var t=e._readableState;if(t.length>0)throw new Error('"endReadable()" called on non-empty stream');t.endEmitted||(t.ended=!0,i.nextTick(M,t,e))}function M(e,t){e.endEmitted||0!==e.length||(e.endEmitted=!0,t.readable=!1,t.emit("end"))}function D(e,t){for(var r=0,n=e.length;r=t.highWaterMark||t.ended))return p("read: emitReadable",t.length,t.ended),0===t.length&&t.ended?B(this):S(this),null;if(0===(e=k(e,t))&&t.ended)return 0===t.length&&B(this),null;var n,i=t.needReadable;return p("need readable",i),(0===t.length||t.length-e0?P(e,t):null)?(t.needReadable=!0,e=0):t.length-=e,0===t.length&&(t.ended||(t.needReadable=!0),r!==e&&t.ended&&B(this)),null!==n&&this.emit("data",n),n},w.prototype._read=function(e){this.emit("error",new Error("_read() is not implemented"))},w.prototype.pipe=function(e,t){var r=this,a=this._readableState;switch(a.pipesCount){case 0:a.pipes=e;break;case 1:a.pipes=[a.pipes,e];break;default:a.pipes.push(e)}a.pipesCount+=1,p("pipe count=%d opts=%j",a.pipesCount,t);var c=(!t||!1!==t.end)&&e!==n.stdout&&e!==n.stderr?l:w;function u(t,n){p("onunpipe"),t===r&&n&&!1===n.hasUnpiped&&(n.hasUnpiped=!0,p("cleanup"),e.removeListener("close",b),e.removeListener("finish",y),e.removeListener("drain",d),e.removeListener("error",g),e.removeListener("unpipe",u),r.removeListener("end",l),r.removeListener("end",w),r.removeListener("data",m),f=!0,!a.awaitDrain||e._writableState&&!e._writableState.needDrain||d())}function l(){p("onend"),e.end()}a.endEmitted?i.nextTick(c):r.once("end",c),e.on("unpipe",u);var d=function(e){return function(){var t=e._readableState;p("pipeOnDrain",t.awaitDrain),t.awaitDrain&&t.awaitDrain--,0===t.awaitDrain&&o(e,"data")&&(t.flowing=!0,A(e))}}(r);e.on("drain",d);var f=!1;var h=!1;function m(t){p("ondata"),h=!1,!1!==e.write(t)||h||((1===a.pipesCount&&a.pipes===e||a.pipesCount>1&&-1!==D(a.pipes,e))&&!f&&(p("false write response, pause",r._readableState.awaitDrain),r._readableState.awaitDrain++,h=!0),r.pause())}function g(t){p("onerror",t),w(),e.removeListener("error",g),0===o(e,"error")&&e.emit("error",t)}function b(){e.removeListener("finish",y),w()}function y(){p("onfinish"),e.removeListener("close",b),w()}function w(){p("unpipe"),r.unpipe(e)}return r.on("data",m),function(e,t,r){if("function"==typeof e.prependListener)return e.prependListener(t,r);e._events&&e._events[t]?s(e._events[t])?e._events[t].unshift(r):e._events[t]=[r,e._events[t]]:e.on(t,r)}(e,"error",g),e.once("close",b),e.once("finish",y),e.emit("pipe",r),a.flowing||(p("pipe resume"),r.resume()),e},w.prototype.unpipe=function(e){var t=this._readableState,r={hasUnpiped:!1};if(0===t.pipesCount)return this;if(1===t.pipesCount)return e&&e!==t.pipes?this:(e||(e=t.pipes),t.pipes=null,t.pipesCount=0,t.flowing=!1,e&&e.emit("unpipe",this,r),this);if(!e){var n=t.pipes,i=t.pipesCount;t.pipes=null,t.pipesCount=0,t.flowing=!1;for(var a=0;a>5==6?2:e>>4==14?3:e>>3==30?4:e>>6==2?-1:-2}function o(e){var t=this.lastTotal-this.lastNeed,r=function(e,t,r){if(128!=(192&t[0]))return e.lastNeed=0,"�";if(e.lastNeed>1&&t.length>1){if(128!=(192&t[1]))return e.lastNeed=1,"�";if(e.lastNeed>2&&t.length>2&&128!=(192&t[2]))return e.lastNeed=2,"�"}}(this,e);return void 0!==r?r:this.lastNeed<=e.length?(e.copy(this.lastChar,t,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal)):(e.copy(this.lastChar,t,0,e.length),void(this.lastNeed-=e.length))}function c(e,t){if((e.length-t)%2==0){var r=e.toString("utf16le",t);if(r){var n=r.charCodeAt(r.length-1);if(n>=55296&&n<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1],r.slice(0,-1)}return r}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=e[e.length-1],e.toString("utf16le",t,e.length-1)}function u(e){var t=e&&e.length?this.write(e):"";if(this.lastNeed){var r=this.lastTotal-this.lastNeed;return t+this.lastChar.toString("utf16le",0,r)}return t}function l(e,t){var r=(e.length-t)%3;return 0===r?e.toString("base64",t):(this.lastNeed=3-r,this.lastTotal=3,1===r?this.lastChar[0]=e[e.length-1]:(this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1]),e.toString("base64",t,e.length-r))}function d(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+this.lastChar.toString("base64",0,3-this.lastNeed):t}function f(e){return e.toString(this.encoding)}function p(e){return e&&e.length?this.write(e):""}t.StringDecoder=a,a.prototype.write=function(e){if(0===e.length)return"";var t,r;if(this.lastNeed){if(void 0===(t=this.fillLast(e)))return"";r=this.lastNeed,this.lastNeed=0}else r=0;return r=0)return i>0&&(e.lastNeed=i-1),i;if(--n=0)return i>0&&(e.lastNeed=i-2),i;if(--n=0)return i>0&&(2===i?i=0:e.lastNeed=i-3),i;return 0}(this,e,t);if(!this.lastNeed)return e.toString("utf8",t);this.lastTotal=r;var n=e.length-(r-this.lastNeed);return e.copy(this.lastChar,0,n),e.toString("utf8",t,n)},a.prototype.fillLast=function(e){if(this.lastNeed<=e.length)return e.copy(this.lastChar,this.lastTotal-this.lastNeed,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal);e.copy(this.lastChar,this.lastTotal-this.lastNeed,0,e.length),this.lastNeed-=e.length}},function(e,t,r){"use strict";e.exports=s;var n=r(13),i=r(19);function a(e,t){var r=this._transformState;r.transforming=!1;var n=r.writecb;if(!n)return this.emit("error",new Error("write callback called multiple times"));r.writechunk=null,r.writecb=null,null!=t&&this.push(t),n(e);var i=this._readableState;i.reading=!1,(i.needReadable||i.length({packetType:e.readUInt8(t),vorbis:new n.StringType(6,"ascii").get(e,t+1)})},t.IdentificationHeader={len:23,get:(e,t)=>({version:e.readUInt32LE(t+0),channelMode:e.readUInt8(t+4),sampleRate:e.readUInt32LE(t+5),bitrateMax:e.readUInt32LE(t+9),bitrateNominal:e.readUInt32LE(t+13),bitrateMin:e.readUInt32LE(t+17)})}}).call(this,r(3).Buffer)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(5),a=r(2)("music-metadata:parser:MP4:atom");t.Header={len:8,get:(e,t)=>{const r=n.UINT32_BE.get(e,t);if(r<0)throw new Error("Invalid atom header length");return{length:r,name:i.FourCcToken.get(e,t+4)}},put:(e,t,r)=>(n.UINT32_BE.put(e,t,r.length),i.FourCcToken.put(e,t+4,r.name))},t.ExtendedSize=n.UINT64_BE,t.ftyp={len:4,get:(e,t)=>({type:new n.StringType(4,"ascii").get(e,t)})},t.mhdr={len:8,get:(e,t)=>({version:n.UINT8.get(e,t+0),flags:n.UINT24_BE.get(e,t+1),nextItemID:n.UINT32_BE.get(e,t+4)})};class s{constructor(e,t,r){if(this.len=e,et&&a(`Warning: atom ${r} expected to be ${t}, but was actually ${e} bytes long.`)}}t.FixedLengthAtom=s;t.MdhdAtom=class extends s{constructor(e){super(e,24,"mdhd"),this.len=e}get(e,t){return{version:n.UINT8.get(e,t+0),flags:n.UINT24_BE.get(e,t+1),creationTime:n.UINT32_BE.get(e,t+4),modificationTime:n.UINT32_BE.get(e,t+8),timeScale:n.UINT32_BE.get(e,t+12),duration:n.UINT32_BE.get(e,t+16),language:n.UINT16_BE.get(e,t+20),quality:n.UINT16_BE.get(e,t+22)}}};t.MvhdAtom=class extends s{constructor(e){super(e,100,"mvhd"),this.len=e}get(e,t){return{version:n.UINT8.get(e,t+0),flags:n.UINT24_BE.get(e,t+1),creationTime:n.UINT32_BE.get(e,t+4),modificationTime:n.UINT32_BE.get(e,t+8),timeScale:n.UINT32_BE.get(e,t+12),duration:n.UINT32_BE.get(e,t+16),preferredRate:n.UINT32_BE.get(e,t+20),preferredVolume:n.UINT16_BE.get(e,t+24),previewTime:n.UINT32_BE.get(e,t+72),previewDuration:n.UINT32_BE.get(e,t+76),posterTime:n.UINT32_BE.get(e,t+80),selectionTime:n.UINT32_BE.get(e,t+84),selectionDuration:n.UINT32_BE.get(e,t+88),currentTime:n.UINT32_BE.get(e,t+92),nextTrackID:n.UINT32_BE.get(e,t+96)}}};t.DataAtom=class{constructor(e){this.len=e}get(e,t){return{type:{set:n.UINT8.get(e,t+0),type:n.UINT24_BE.get(e,t+1)},locale:n.UINT24_BE.get(e,t+4),value:new n.BufferType(this.len-8).get(e,t+8)}}};t.NameAtom=class{constructor(e){this.len=e}get(e,t){return{version:n.UINT8.get(e,t),flags:n.UINT24_BE.get(e,t+1),name:new n.StringType(this.len-4,"utf-8").get(e,t+4)}}};t.TrackHeaderAtom=class{constructor(e){this.len=e}get(e,t){return{version:n.UINT8.get(e,t),flags:n.UINT24_BE.get(e,t+1),creationTime:n.UINT32_BE.get(e,t+4),modificationTime:n.UINT32_BE.get(e,t+8),trackId:n.UINT32_BE.get(e,t+12),duration:n.UINT32_BE.get(e,t+20),layer:n.UINT16_BE.get(e,t+24),alternateGroup:n.UINT16_BE.get(e,t+26),volume:n.UINT16_BE.get(e,t+28)}}};const o={len:8,get:(e,t)=>({version:n.UINT8.get(e,t+0),flags:n.UINT24_BE.get(e,t+1),numberOfEntries:n.UINT32_BE.get(e,t+4)})};class c{constructor(e){this.len=e}get(e,t){return{dataFormat:i.FourCcToken.get(e,t),dataReferenceIndex:n.UINT16_BE.get(e,t+10),description:new n.BufferType(this.len-12).get(e,t+12)}}}t.StsdAtom=class{constructor(e){this.len=e}get(e,t){const r=o.get(e,t);t+=o.len;const i=[];for(let a=0;a({version:n.INT16_BE.get(e,t),revision:n.INT16_BE.get(e,t+2),vendor:n.INT32_BE.get(e,t+4)})},t.SoundSampleDescriptionV0={len:12,get:(e,t)=>({numAudioChannels:n.INT16_BE.get(e,t+0),sampleSize:n.INT16_BE.get(e,t+2),compressionId:n.INT16_BE.get(e,t+4),packetSize:n.INT16_BE.get(e,t+6),sampleRate:n.UINT16_BE.get(e,t+8)+n.UINT16_BE.get(e,t+10)/1e4})}},,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(189),i=r(192),a=r(270),s=r(271),o=n("music-metadata-browser:main");t.parseNodeStream=i.parseStream,t.parseReadableStream=async function(e,r,n){const i=new a.ReadableWeToNodeStream(e),s=await t.parseNodeStream(i,r,n);return o(`Completed parsing from stream bytesRead=${i.bytesRead} / fileSize=${n&&n.fileSize?n.fileSize:"?"}`),await i.close(),s},t.parseBuffer=i.parseBuffer,t.parseBlob=function(e,t){return function(e){return new Promise((t,r)=>{const n=new FileReader;n.onloadend=e=>{let r=e.target.result;r instanceof ArrayBuffer&&(r=s(new Uint8Array(e.target.result))),t(r)},n.onerror=e=>{r(new Error(e.type))},n.onabort=e=>{r(new Error(e.type))},n.readAsArrayBuffer(e)})}(e).then(r=>i.parseBuffer(r,e.type,t))},t.fetchFromUrl=async function(e,t){const r=await fetch(e),n=r.headers.get("Content-Type"),i=[];if(r.headers.forEach(e=>{i.push(e)}),r.ok){if(r.body){const e=await this.parseReadableStream(r.body,n,t);return o("Closing HTTP-readable-stream..."),r.body.locked||await r.body.cancel(),o("HTTP-readable-stream closed."),e}return this.parseBlob(await r.blob(),t)}throw new Error(`HTTP error status=${r.status}: ${r.statusText}`)},t.parseFromTokenizer=i.parseFromTokenizer,t.orderTags=i.orderTags,t.ratingToStars=i.ratingToStars},function(e,t,r){var i;(function(){var r=!1,a=function(e){return e instanceof a?e:this instanceof a?void(this.EXIFwrapped=e):new a(e)};e.exports&&(t=e.exports=a),t.EXIF=a;var s=a.Tags={36864:"ExifVersion",40960:"FlashpixVersion",40961:"ColorSpace",40962:"PixelXDimension",40963:"PixelYDimension",37121:"ComponentsConfiguration",37122:"CompressedBitsPerPixel",37500:"MakerNote",37510:"UserComment",40964:"RelatedSoundFile",36867:"DateTimeOriginal",36868:"DateTimeDigitized",37520:"SubsecTime",37521:"SubsecTimeOriginal",37522:"SubsecTimeDigitized",33434:"ExposureTime",33437:"FNumber",34850:"ExposureProgram",34852:"SpectralSensitivity",34855:"ISOSpeedRatings",34856:"OECF",37377:"ShutterSpeedValue",37378:"ApertureValue",37379:"BrightnessValue",37380:"ExposureBias",37381:"MaxApertureValue",37382:"SubjectDistance",37383:"MeteringMode",37384:"LightSource",37385:"Flash",37396:"SubjectArea",37386:"FocalLength",41483:"FlashEnergy",41484:"SpatialFrequencyResponse",41486:"FocalPlaneXResolution",41487:"FocalPlaneYResolution",41488:"FocalPlaneResolutionUnit",41492:"SubjectLocation",41493:"ExposureIndex",41495:"SensingMethod",41728:"FileSource",41729:"SceneType",41730:"CFAPattern",41985:"CustomRendered",41986:"ExposureMode",41987:"WhiteBalance",41988:"DigitalZoomRation",41989:"FocalLengthIn35mmFilm",41990:"SceneCaptureType",41991:"GainControl",41992:"Contrast",41993:"Saturation",41994:"Sharpness",41995:"DeviceSettingDescription",41996:"SubjectDistanceRange",40965:"InteroperabilityIFDPointer",42016:"ImageUniqueID"},o=a.TiffTags={256:"ImageWidth",257:"ImageHeight",34665:"ExifIFDPointer",34853:"GPSInfoIFDPointer",40965:"InteroperabilityIFDPointer",258:"BitsPerSample",259:"Compression",262:"PhotometricInterpretation",274:"Orientation",277:"SamplesPerPixel",284:"PlanarConfiguration",530:"YCbCrSubSampling",531:"YCbCrPositioning",282:"XResolution",283:"YResolution",296:"ResolutionUnit",273:"StripOffsets",278:"RowsPerStrip",279:"StripByteCounts",513:"JPEGInterchangeFormat",514:"JPEGInterchangeFormatLength",301:"TransferFunction",318:"WhitePoint",319:"PrimaryChromaticities",529:"YCbCrCoefficients",532:"ReferenceBlackWhite",306:"DateTime",270:"ImageDescription",271:"Make",272:"Model",305:"Software",315:"Artist",33432:"Copyright"},c=a.GPSTags={0:"GPSVersionID",1:"GPSLatitudeRef",2:"GPSLatitude",3:"GPSLongitudeRef",4:"GPSLongitude",5:"GPSAltitudeRef",6:"GPSAltitude",7:"GPSTimeStamp",8:"GPSSatellites",9:"GPSStatus",10:"GPSMeasureMode",11:"GPSDOP",12:"GPSSpeedRef",13:"GPSSpeed",14:"GPSTrackRef",15:"GPSTrack",16:"GPSImgDirectionRef",17:"GPSImgDirection",18:"GPSMapDatum",19:"GPSDestLatitudeRef",20:"GPSDestLatitude",21:"GPSDestLongitudeRef",22:"GPSDestLongitude",23:"GPSDestBearingRef",24:"GPSDestBearing",25:"GPSDestDistanceRef",26:"GPSDestDistance",27:"GPSProcessingMethod",28:"GPSAreaInformation",29:"GPSDateStamp",30:"GPSDifferential"},u=a.IFD1Tags={256:"ImageWidth",257:"ImageHeight",258:"BitsPerSample",259:"Compression",262:"PhotometricInterpretation",273:"StripOffsets",274:"Orientation",277:"SamplesPerPixel",278:"RowsPerStrip",279:"StripByteCounts",282:"XResolution",283:"YResolution",284:"PlanarConfiguration",296:"ResolutionUnit",513:"JpegIFOffset",514:"JpegIFByteCount",529:"YCbCrCoefficients",530:"YCbCrSubSampling",531:"YCbCrPositioning",532:"ReferenceBlackWhite"},l=a.StringValues={ExposureProgram:{0:"Not defined",1:"Manual",2:"Normal program",3:"Aperture priority",4:"Shutter priority",5:"Creative program",6:"Action program",7:"Portrait mode",8:"Landscape mode"},MeteringMode:{0:"Unknown",1:"Average",2:"CenterWeightedAverage",3:"Spot",4:"MultiSpot",5:"Pattern",6:"Partial",255:"Other"},LightSource:{0:"Unknown",1:"Daylight",2:"Fluorescent",3:"Tungsten (incandescent light)",4:"Flash",9:"Fine weather",10:"Cloudy weather",11:"Shade",12:"Daylight fluorescent (D 5700 - 7100K)",13:"Day white fluorescent (N 4600 - 5400K)",14:"Cool white fluorescent (W 3900 - 4500K)",15:"White fluorescent (WW 3200 - 3700K)",17:"Standard light A",18:"Standard light B",19:"Standard light C",20:"D55",21:"D65",22:"D75",23:"D50",24:"ISO studio tungsten",255:"Other"},Flash:{0:"Flash did not fire",1:"Flash fired",5:"Strobe return light not detected",7:"Strobe return light detected",9:"Flash fired, compulsory flash mode",13:"Flash fired, compulsory flash mode, return light not detected",15:"Flash fired, compulsory flash mode, return light detected",16:"Flash did not fire, compulsory flash mode",24:"Flash did not fire, auto mode",25:"Flash fired, auto mode",29:"Flash fired, auto mode, return light not detected",31:"Flash fired, auto mode, return light detected",32:"No flash function",65:"Flash fired, red-eye reduction mode",69:"Flash fired, red-eye reduction mode, return light not detected",71:"Flash fired, red-eye reduction mode, return light detected",73:"Flash fired, compulsory flash mode, red-eye reduction mode",77:"Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",79:"Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",89:"Flash fired, auto mode, red-eye reduction mode",93:"Flash fired, auto mode, return light not detected, red-eye reduction mode",95:"Flash fired, auto mode, return light detected, red-eye reduction mode"},SensingMethod:{1:"Not defined",2:"One-chip color area sensor",3:"Two-chip color area sensor",4:"Three-chip color area sensor",5:"Color sequential area sensor",7:"Trilinear sensor",8:"Color sequential linear sensor"},SceneCaptureType:{0:"Standard",1:"Landscape",2:"Portrait",3:"Night scene"},SceneType:{1:"Directly photographed"},CustomRendered:{0:"Normal process",1:"Custom process"},WhiteBalance:{0:"Auto white balance",1:"Manual white balance"},GainControl:{0:"None",1:"Low gain up",2:"High gain up",3:"Low gain down",4:"High gain down"},Contrast:{0:"Normal",1:"Soft",2:"Hard"},Saturation:{0:"Normal",1:"Low saturation",2:"High saturation"},Sharpness:{0:"Normal",1:"Soft",2:"Hard"},SubjectDistanceRange:{0:"Unknown",1:"Macro",2:"Close view",3:"Distant view"},FileSource:{3:"DSC"},Components:{0:"",1:"Y",2:"Cb",3:"Cr",4:"R",5:"G",6:"B"}};function d(e){return!!e.exifdata}function f(e,t){function n(n){var i=p(n);e.exifdata=i||{};var s=function(e){var t=new DataView(e);r&&console.log("Got file of length "+e.byteLength);if(255!=t.getUint8(0)||216!=t.getUint8(1))return r&&console.log("Not a valid JPEG"),!1;var n=2,i=e.byteLength,a=function(e,t){return 56===e.getUint8(t)&&66===e.getUint8(t+1)&&73===e.getUint8(t+2)&&77===e.getUint8(t+3)&&4===e.getUint8(t+4)&&4===e.getUint8(t+5)};for(;n")+8,l=(c=c.substring(c.indexOf("4?p:t+8,s=[],c=0;c4?p:t+8,f-1);case 3:if(1==f)return e.getUint16(t+8,!i);for(a=f>2?p:t+8,s=[],c=0;ce.byteLength)return{};var a=g(e,t,t+i,u,n);if(a.Compression)switch(a.Compression){case 6:if(a.JpegIFOffset&&a.JpegIFByteCount){var s=t+a.JpegIFOffset,o=a.JpegIFByteCount;a.blob=new Blob([new Uint8Array(e.buffer,s,o)],{type:"image/jpeg"})}break;case 1:console.log("Thumbnail image format is TIFF, which is not implemented.");break;default:console.log("Unknown thumbnail image format '%s'",a.Compression)}else 2==a.PhotometricInterpretation&&console.log("Thumbnail image format is RGB, which is not implemented.");return a}(e,p,h,n),i}function T(e){var t={};if(1==e.nodeType){if(e.attributes.length>0){t["@attributes"]={};for(var r=0;r0)for(var r=0;r{if(!t.isBuffer(e))return{};let i={},a=e.indexOf(n);for(;-1!==a;){let t=(a=e.indexOf(n,a+n.byteLength))+n.byteLength,s=t+1,o=s+2,c=e.readUInt8(t),u=e.readUInt16BE(s);if(!r.has(c))continue;if(u>e.length-(o+u))throw new Error("Invalid IPTC directory");let l=r.get(c),d=e.slice(o,o+u).toString();null==i[l]?i[l]=d:Array.isArray(i[l])?i[l].push(d):i[l]=[i[l],d]}return i}}).call(this,r(3).Buffer)},,,,,,,,,function(e,t,r){e.exports=r(186)},function(e,t,r){"use strict";r.r(t),function(e){var t=r(174),n=r(175),i=r.n(n),a=r(176),s=r.n(a);window.mediaCloudReaderCache={readers:[],getFreeReader:function(){return this.readers.length>0?(console.log("Got cached reader."),this.readers.splice(0,1)[0]):new FileReader},cacheReader:function(e){this.readers.push(e)}},window.DirectUploadItem=Backbone.Model.extend({initialize:function(e){this.on("change:progress",this.updateProgress,this)},updateProgress:function(){var e=this.get("attachment"),t=this.get("progress");e.set({percent:t})},startUpload:function(){if("uploadToStorage"in DirectUploadItem.prototype){var e=this.get("file"),t=this.get("faces"),r=this.get("mimeType");this.get("isDirectUpload")?(console.log("Uploading to Cloud Storage"),this.uploadToStorage(e,t,r,this)):this.uploadToWordPress(e,t,this)}else alert("Direct upload is misconfigured.")},uploadToWordPress:function(e,t,r){console.log("Uploading to WordPress");var n=new FormData;n.append("action",_wpPluploadSettings.defaults.multipart_params.action),n.append("_wpnonce",_wpPluploadSettings.defaults.multipart_params._wpnonce),n.append("name",e.name),n.append("async-upload",e),jQuery.ajax({url:_wpPluploadSettings.defaults.url,method:"POST",contentType:!1,processData:!1,data:n,dataType:"json",xhr:function(){var e=jQuery.ajaxSettings.xhr();return e.upload.onprogress=function(e){r.set({progress:e.loaded/e.total*100})}.bind(this),e},success:function(e){e.hasOwnProperty("success")&&e.success?r.wordpressUploadFinished(e,t):r.uploadError()},error:function(e){r.uploadError()}})},finishWordpressUpload:function(e){var t=this.get("attachment");_.each(["file","loaded","size","percent"],function(e){t.unset(e)}),t.set(_.extend(e.data,{uploading:!1})),wp.media.model.Attachment.get(e.data.id,t),this.set({state:"done"}),wp.Uploader.queue.remove(t),wp.Uploader.queue.all(function(e){return!e.get("uploading")})&&wp.Uploader.queue.reset()},wordpressUploadFinished:function(e,t){if(null!=t){var r={action:"ilab_add_face_data",post_id:e.data.id,faces:t};jQuery.post(ajaxurl,r,function(t){this.finishWordpressUpload(e)}.bind(this))}else this.finishWordpressUpload(e)},doUploadFinished:function(e,t,r){var n=this.get("attachment"),i={action:"ilab_upload_import_cloud_file",key:e};null!=r&&(i.metadata=r),console.log("Upload finished"),null!=t&&t.length>0&&(i.faces=t),jQuery.post(ajaxurl,i,function(e){_.each(["file","loaded","size","percent"],function(e){n.unset(e)}),"success"==e.status?(n.set(_.extend(e.attachment,{uploading:!1})),wp.media.model.Attachment.get(e.attachment.id,n),this.set({state:"done"})):this.set({state:"error"}),wp.Uploader.queue.remove(n),wp.Uploader.queue.all(function(e){return!e.get("uploading")})&&wp.Uploader.queue.reset()}.bind(this))},mapID3Meta:function(e,t,r,n){r.hasOwnProperty(e)&&(n[t]=r[e])},mapIndexedID3Meta:function(e,t,r,n){r.hasOwnProperty(e)&&r[e].length>0&&(n[t]=r[e][0])},assignAudioMetadata:function(e,t){if(this.mapID3Meta("album","album",e.common,t),this.mapID3Meta("artist","artist",e.common,t),this.mapIndexedID3Meta("genre","genre",e.common,t),this.mapID3Meta("title","title",e.common,t),e.common.hasOwnProperty("track")&&null!=e.common.track.no&&(t.track_number=e.common.track.no),this.mapID3Meta("year","year",e.common,t),this.mapIndexedID3Meta("composer","composer",e.common,t),this.mapIndexedID3Meta("lyricist","lyricist",e.common,t),this.mapIndexedID3Meta("writer","writer",e.common,t),this.mapIndexedID3Meta("conductor","conductor",e.common,t),this.mapIndexedID3Meta("remixer","remixer",e.common,t),this.mapIndexedID3Meta("arranger","arranger",e.common,t),this.mapIndexedID3Meta("engineer","engineer",e.common,t),this.mapIndexedID3Meta("producer","producer",e.common,t),this.mapIndexedID3Meta("djmixer","dj_mixer",e.common,t),this.mapIndexedID3Meta("mixer","mixer",e.common,t),this.mapIndexedID3Meta("technician","technician",e.common,t),this.mapIndexedID3Meta("label","label",e.common,t),this.mapIndexedID3Meta("subtitle","subtitle",e.common,t),this.mapIndexedID3Meta("compilation","compilation",e.common,t),this.mapID3Meta("bpm","bpm",e.common,t),this.mapID3Meta("mood","mood",e.common,t),this.mapID3Meta("media","media",e.common,t),this.mapID3Meta("tvShow","tv_show",e.common,t),this.mapID3Meta("tvSeason","tv_season",e.common,t),this.mapID3Meta("tvEpisode","tv_episode",e.common,t),this.mapID3Meta("tvNetwork","tv_network",e.common,t),this.mapID3Meta("podcast","podcast",e.common,t),this.mapID3Meta("podcasturl","podcast_url",e.common,t),this.mapID3Meta("releasestatus","release_status",e.common,t),this.mapID3Meta("releasetype","release_type",e.common,t),this.mapID3Meta("releasecountry","release_country",e.common,t),this.mapID3Meta("language","language",e.common,t),this.mapID3Meta("copyright","copyright",e.common,t),this.mapID3Meta("license","license",e.common,t),this.mapID3Meta("encodedby","encoded_by",e.common,t),this.mapID3Meta("encodersettings","encoder_options",e.common,t),this.mapID3Meta("gapless","gapless",e.common,t),this.mapID3Meta("barcode","barcode",e.common,t),this.mapID3Meta("asin","asin",e.common,t),this.mapID3Meta("website","website",e.common,t),this.mapID3Meta("averageLevel","average_level",e.common,t),this.mapID3Meta("peakLevel","peak_level",e.common,t),this.mapID3Meta("bitrate","bitrate",e.format,t),this.mapID3Meta("codec","dataformat",e.format,t),this.mapID3Meta("codecProfile","bitrate_mode",e.format,t),this.mapID3Meta("lossless","lossless",e.format,t),this.mapID3Meta("numberOfChannels","channels",e.format,t),this.mapID3Meta("duration","length",e.format,t),this.mapID3Meta("sampleRate","sample_rate",e.format,t),e.common.hasOwnProperty("picture")&&e.common.picture.length>0){var r=e.common.picture[0];t.thumbnail={mimeType:r.format,data:r.data.toString("base64")},t.image=[{mime:r.format}]}},mapImageMeta:function(e,t,r,n,i){if(t.hasOwnProperty(r)){var a=t[r];if("string"==e){if(!(o="string"==typeof a||a instanceof String))return;if(/[\x00-\x1F]/.test(a))return;n[i]=a.trim()}else if("strings"==e){if(o="string"==typeof a||a instanceof String){a=a.split(",");for(var s=0;s0&&(n[i]=a)}else if("date"==e){var o;if(!(o="string"==typeof a||a instanceof String))return;var c=a.split(" ");if(2!=c.length)return;var u=c[0].split(":");if(3!=u.length)return;var l=u[0],d=u[1],f=u[2],p=new Date("".concat(d,"/").concat(f,"/").concat(l," ").concat(c[1]));n[i]=(p.getTime()/1e3).toFixed(0)}else if("frac"==e){if(o)return;if(!("number"==typeof a||a instanceof Number))return;n[i]=a.numerator/a.denominator}else if("number"==e){if(o)return;if(!("number"==typeof a||a instanceof Number))return;n[i]=a}}},assignImageMetadata:function(e,t,r){var n={};this.mapImageMeta("string",e,"headline",n,"title"),this.mapImageMeta("string",e,"caption",n,"caption"),this.mapImageMeta("string",e,"credit",n,"credit"),this.mapImageMeta("string",e,"copyright",n,"copyright"),this.mapImageMeta("strings",e,"keywords",n,"keywords"),0!=t&&(this.mapImageMeta("string",t,"ImageDescription",n,"title"),n.hasOwnProperty("caption")||this.mapImageMeta("bytes",t,"UserComment",n,"caption"),n.hasOwnProperty("copyright")||this.mapImageMeta("bytes",t,"Copyright",n,"copyright"),this.mapImageMeta("string",t,"Artist",n,"credit"),n.hasOwnProperty("credit")||this.mapImageMeta("string",t,"Author",n,"credit"),this.mapImageMeta("string",t,"Copyright",n,"copyright"),this.mapImageMeta("string",t,"Model",n,"camera"),this.mapImageMeta("string",t,"ISOSpeedRatings",n,"iso"),this.mapImageMeta("number",t,"ISOSpeedRatings",n,"iso"),this.mapImageMeta("date",t,"DateTimeDigitized",n,"created_timestamp"),this.mapImageMeta("frac",t,"FocalLength",n,"focal_length"),this.mapImageMeta("frac",t,"FNumber",n,"aperture"),this.mapImageMeta("frac",t,"ExposureTime",n,"shutter_speed"),this.mapImageMeta("number",t,"Orientation",n,"orientation")),r.image_meta=n},uploadFinished:function(r,n){var a=this.get("attachment").get("file"),o={filesize:a.size},c=this.get("mimeType");if(0===c.indexOf("audio/")){var u=c.split("/")[1];o.fileformat=u,t.parseBlob(a,{duration:!0}).then(function(e){this.assignAudioMetadata(e,o),this.doUploadFinished(r,n,o)}.bind(this)).catch(function(e){this.doUploadFinished(r,n,o)}.bind(this))}else if(0==c.indexOf("image/")){var l=mediaCloudReaderCache.getFreeReader();l.onload=function(t){var a={};try{a=s()(e.from(l.result))}catch(e){console.log("IPTC Error",e)}var c=i.a.readFromBinaryFile(l.result);this.assignImageMetadata(a,c,o),mediaCloudReaderCache.cacheReader(l),this.doUploadFinished(r,n,o)}.bind(this),l.onerror=function(e){mediaCloudReaderCache.cacheReader(l),this.doUploadFinished(r,n,o)}.bind(this),l.readAsArrayBuffer(a)}else this.doUploadFinished(r,n,o)},uploadError:function(){this.set({state:"error"});var e=this.get("attachment");_.each(["file","loaded","size","percent"],function(t){e.unset(t)}),wp.Uploader.queue.remove(e),e.destroy()}}),window.DirectUploader=Backbone.Model.extend({initialize:function(e){this.totalFilesDropped=0,this.totalFilesUploaded=0,this.files=[],this.toBulkProcess=[],this.uploadingFiles=[],this.watchToken=0,this.settings=mediaCloudDirectUploadSettings},queueNext:function(){if(clearTimeout(this.watchToken),this.uploadingFiles.length0){for(var e=this.settings.maxUploads-this.uploadingFiles.length,t=0;t0){var r=this.files.shift();this.uploadingFiles.push(r),r.startUpload()}}else if(0==this.uploadingFiles.length&&this.toBulkProcess.length>0&&this.totalFilesDropped==this.totalFilesUploaded){var n={action:"ilab_upload_process_batch",batch:this.toBulkProcess};this.toBulkProcess=[],this.totalFilesDropped=0,this.totalFilesUploaded=0,jQuery.post(ajaxurl,n,function(e){})}this.watchToken=setTimeout(this.queueNext.bind(this),500)},addFile:function(e){if(""==e.type)return!1;var t=e.type;"application/x-photoshop"==t&&(t="image/psd");var r=-1!=this.settings.allowedMimes.indexOf(t);console.log("Is Direct Upload?",r);var n=t.split("/"),i=n[0],a=n[1];a="jpg"==a?"jpeg":a,this.totalFilesDropped++;var s={file:e,uploading:!0,date:new Date,filename:e.name,isDirectUpload:r,menuOrder:0,uploadedTo:wp.media.model.settings.post.id,loaded:0,size:e.size,percent:0,type:i,subType:a},o=wp.media.model.Attachment.create(s);wp.Uploader.queue.add(o);var c=new DirectUploadItem({attachment:o,file:e,mimeType:t,isDirectUpload:r,progress:0,driver:this.settings.driver,faces:null,state:"waiting"});c.on("change:state",function(e,t){if("done"==t||"error"==t){this.totalFilesUploaded++,"done"==t&&this.toBulkProcess.push(o.id);var r=this.files.indexOf(c);r>-1&&this.files.splice(r),(r=this.uploadingFiles.indexOf(c))>-1&&this.uploadingFiles.splice(r),this.queueNext()}}.bind(this)),"undefined"!=typeof ILABFaceDetector?ILABFaceDetector(e,function(e){c.set("faces",e),this.files.push(c)}.bind(this)):this.files.push(c)}}),wp.media.view.UploaderWindow.prototype.__ready=wp.media.view.UploaderWindow.prototype.ready,wp.media.view.UploaderWindow.prototype.ready=function(){this.__ready(),this.directUploader=new DirectUploader({}),this.uploader.uploader.unbind("FilesAdded"),this.uploader.uploader.bind("FilesAdded",function(e,t){_.each(t,function(e){this.directUploader.addFile(e.getNative())}.bind(this)),this.directUploader.queueNext()}.bind(this))},wp.media.view.UploaderInline=wp.media.view.UploaderInline.extend({template:wp.template("media-cloud-direct-upload")})}.call(this,r(3).Buffer)},function(e,t,r){"use strict";t.byteLength=function(e){var t=u(e),r=t[0],n=t[1];return 3*(r+n)/4-n},t.toByteArray=function(e){for(var t,r=u(e),n=r[0],s=r[1],o=new a(function(e,t,r){return 3*(t+r)/4-r}(0,n,s)),c=0,l=s>0?n-4:n,d=0;d>16&255,o[c++]=t>>8&255,o[c++]=255&t;2===s&&(t=i[e.charCodeAt(d)]<<2|i[e.charCodeAt(d+1)]>>4,o[c++]=255&t);1===s&&(t=i[e.charCodeAt(d)]<<10|i[e.charCodeAt(d+1)]<<4|i[e.charCodeAt(d+2)]>>2,o[c++]=t>>8&255,o[c++]=255&t);return o},t.fromByteArray=function(e){for(var t,r=e.length,i=r%3,a=[],s=0,o=r-i;so?o:s+16383));1===i?(t=e[r-1],a.push(n[t>>2]+n[t<<4&63]+"==")):2===i&&(t=(e[r-2]<<8)+e[r-1],a.push(n[t>>10]+n[t>>4&63]+n[t<<2&63]+"="));return a.join("")};for(var n=[],i=[],a="undefined"!=typeof Uint8Array?Uint8Array:Array,s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",o=0,c=s.length;o0)throw new Error("Invalid string. Length must be a multiple of 4");var r=e.indexOf("=");return-1===r&&(r=t),[r,r===t?0:4-r%4]}function l(e,t,r){for(var i,a,s=[],o=t;o>18&63]+n[a>>12&63]+n[a>>6&63]+n[63&a]);return s.join("")}i["-".charCodeAt(0)]=62,i["_".charCodeAt(0)]=63},function(e,t){t.read=function(e,t,r,n,i){var a,s,o=8*i-n-1,c=(1<>1,l=-7,d=r?i-1:0,f=r?-1:1,p=e[t+d];for(d+=f,a=p&(1<<-l)-1,p>>=-l,l+=o;l>0;a=256*a+e[t+d],d+=f,l-=8);for(s=a&(1<<-l)-1,a>>=-l,l+=n;l>0;s=256*s+e[t+d],d+=f,l-=8);if(0===a)a=1-u;else{if(a===c)return s?NaN:1/0*(p?-1:1);s+=Math.pow(2,n),a-=u}return(p?-1:1)*s*Math.pow(2,a-n)},t.write=function(e,t,r,n,i,a){var s,o,c,u=8*a-i-1,l=(1<>1,f=23===i?Math.pow(2,-24)-Math.pow(2,-77):0,p=n?0:a-1,h=n?1:-1,m=t<0||0===t&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(o=isNaN(t)?1:0,s=l):(s=Math.floor(Math.log(t)/Math.LN2),t*(c=Math.pow(2,-s))<1&&(s--,c*=2),(t+=s+d>=1?f/c:f*Math.pow(2,1-d))*c>=2&&(s++,c/=2),s+d>=l?(o=0,s=l):s+d>=1?(o=(t*c-1)*Math.pow(2,i),s+=d):(o=t*Math.pow(2,d-1)*Math.pow(2,i),s=0));i>=8;e[r+p]=255&o,p+=h,o/=256,i-=8);for(s=s<0;e[r+p]=255&s,p+=h,s/=256,u-=8);e[r+p-h]|=128*m}},function(e,t,r){(function(n){t.log=function(...e){return"object"==typeof console&&console.log&&console.log(...e)},t.formatArgs=function(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+e.exports.humanize(this.diff),!this.useColors)return;const r="color: "+this.color;t.splice(1,0,r,"color: inherit");let n=0,i=0;t[0].replace(/%[a-zA-Z%]/g,e=>{"%%"!==e&&(n++,"%c"===e&&(i=n))}),t.splice(i,0,r)},t.save=function(e){try{e?t.storage.setItem("debug",e):t.storage.removeItem("debug")}catch(e){}},t.load=function(){let e;try{e=t.storage.getItem("debug")}catch(e){}!e&&void 0!==n&&"env"in n&&(e=n.env.DEBUG);return e},t.useColors=function(){if("undefined"!=typeof window&&window.process&&("renderer"===window.process.type||window.process.__nwjs))return!0;if("undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))return!1;return"undefined"!=typeof document&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||"undefined"!=typeof window&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)},t.storage=function(){try{return localStorage}catch(e){}}(),t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"],e.exports=r(190)(t);const{formatters:i}=e.exports;i.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}}).call(this,r(9))},function(e,t,r){e.exports=function(e){function t(e){let t=0;for(let r=0;r{if("%%"===r)return r;o++;const a=n.formatters[i];if("function"==typeof a){const n=e[o];r=a.call(t,n),e.splice(o,1),o--}return r}),n.formatArgs.call(t,e),(t.log||n.log).apply(t,e)}return s.namespace=e,s.enabled=n.enabled(e),s.useColors=n.useColors(),s.color=t(e),s.destroy=i,s.extend=a,"function"==typeof n.init&&n.init(s),n.instances.push(s),s}function i(){const e=n.instances.indexOf(this);return-1!==e&&(n.instances.splice(e,1),!0)}function a(e,t){const r=n(this.namespace+(void 0===t?":":t)+e);return r.log=this.log,r}function s(e){return e.toString().substring(2,e.toString().length-2).replace(/\.\*\?$/,"*")}return n.debug=n,n.default=n,n.coerce=function(e){return e instanceof Error?e.stack||e.message:e},n.disable=function(){const e=[...n.names.map(s),...n.skips.map(s).map(e=>"-"+e)].join(",");return n.enable(""),e},n.enable=function(e){let t;n.save(e),n.names=[],n.skips=[];const r=("string"==typeof e?e:"").split(/[\s,]+/),i=r.length;for(t=0;t{n[t]=e[t]}),n.instances=[],n.names=[],n.skips=[],n.formatters={},n.selectColor=t,n.enable(n.load()),n}},function(e,t){var r=1e3,n=60*r,i=60*n,a=24*i,s=7*a,o=365.25*a;function c(e,t,r,n){var i=t>=1.5*r;return Math.round(e/r)+" "+n+(i?"s":"")}e.exports=function(e,t){t=t||{};var u=typeof e;if("string"===u&&e.length>0)return function(e){if((e=String(e)).length>100)return;var t=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(!t)return;var c=parseFloat(t[1]);switch((t[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return c*o;case"weeks":case"week":case"w":return c*s;case"days":case"day":case"d":return c*a;case"hours":case"hour":case"hrs":case"hr":case"h":return c*i;case"minutes":case"minute":case"mins":case"min":case"m":return c*n;case"seconds":case"second":case"secs":case"sec":case"s":return c*r;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return c;default:return}}(e);if("number"===u&&isFinite(e))return t.long?function(e){var t=Math.abs(e);if(t>=a)return c(e,t,a,"day");if(t>=i)return c(e,t,i,"hour");if(t>=n)return c(e,t,n,"minute");if(t>=r)return c(e,t,r,"second");return e+" ms"}(e):function(e){var t=Math.abs(e);if(t>=a)return Math.round(e/a)+"d";if(t>=i)return Math.round(e/i)+"h";if(t>=n)return Math.round(e/n)+"m";if(t>=r)return Math.round(e/r)+"s";return e+"ms"}(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(21),i=r(204);function a(e,t,r){return!e.fileSize&&r.fileSize&&(e.fileSize=r.fileSize),i.ParserFactory.parse(e,t,r)}t.parseStream=function(e,t,r={}){return a(n.fromStream(e),t,r)},t.parseBuffer=function(e,t,r={}){return a(n.fromBuffer(e),t,r)},t.parseFromTokenizer=a,t.orderTags=function(e){const t={};for(const r of e)(t[r.id]=t[r.id]||[]).push(r.value);return t},t.ratingToStars=function(e){return void 0===e?0:1+Math.round(4*e)}},function(e,t,r){"use strict";(function(e){Object.defineProperty(t,"__esModule",{value:!0});const n=r(194),i=r(10),a=r(195),s=r(200)("strtok3:ReadStreamTokenizer");t.ReadStreamTokenizer=class extends n.AbstractTokenizer{constructor(e,t){super(),this.streamReader=new a.StreamReader(e),this.fileSize=t}async readBuffer(e,t=0,r=e.length,n){if(0===r)return 0;if(n){const i=n-this.position;if(i>0)return await this.ignore(n-this.position),this.readBuffer(e,t,r);if(i<0)throw new Error("Cannot read from a negative offset in a stream")}let s;try{s=await this.streamReader.read(e,t,r),this.position+=s}catch(e){throw e.message===a.endOfStream?new Error(i.endOfFile):e}if(s0){const a=e.alloc(n+i);return c=await this.peekBuffer(a,0,i+n,void 0,o),a.copy(t,r,i),c-i}if(i<0)throw new Error("Cannot peek from a negative offset in a stream")}try{c=await this.streamReader.peek(t,r,n)}catch(e){throw e.message===a.endOfStream?new Error(i.endOfFile):e}if(!o&&c{this.reject=t,this.resolve=e})}}t.endOfStream="End-Of-Stream";t.StreamReader=class{constructor(e){if(this.s=e,this.endOfStream=!1,this.peekQueue=[],!e.read||!e.once)throw new Error("Expected an instance of stream.Readable");this.s.once("end",()=>this.reject(new Error(t.endOfStream))),this.s.once("error",e=>this.reject(e)),this.s.once("close",()=>this.reject(new Error("Stream closed")))}async peek(e,t,r){const n=await this.read(e,t,r);return this.peekQueue.push(e.slice(t,t+n)),n}async read(e,r,n){if(0===n)return 0;if(0===this.peekQueue.length&&this.endOfStream)throw new Error(t.endOfStream);let i=n,a=0;for(;this.peekQueue.length>0&&i>0;){const t=this.peekQueue.pop(),n=Math.min(t.length,i);t.copy(e,r+a,0,n),a+=n,i-=n,n0&&!this.endOfStream&&(a+=await this._read(e,r+a,i)),a}async _read(e,t,r){n.ok(!this.request,"Concurrent read operation?");const a=this.s.read(r);return a?(a.copy(e,t),a.length):(this.request={buffer:e,offset:t,length:r,deferred:new i},this.s.once("readable",()=>{this.tryRead()}),this.request.deferred.promise.then(e=>(this.request=null,e)).catch(e=>{throw this.request=null,e}))}tryRead(){const e=this.s.read(this.request.length);e?(e.copy(this.request.buffer,this.request.offset),this.request.deferred.resolve(e.length)):this.s.once("readable",()=>{this.tryRead()})}reject(e){this.endOfStream=!0,this.request&&(this.request.deferred.reject(e),this.request=null)}}},function(e,t,r){"use strict";var n=Object.getOwnPropertySymbols,i=Object.prototype.hasOwnProperty,a=Object.prototype.propertyIsEnumerable;function s(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},r=0;r<10;r++)t["_"+String.fromCharCode(r)]=r;if("0123456789"!==Object.getOwnPropertyNames(t).map(function(e){return t[e]}).join(""))return!1;var n={};return"abcdefghijklmnopqrst".split("").forEach(function(e){n[e]=e}),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},n)).join("")}catch(e){return!1}}()?Object.assign:function(e,t){for(var r,o,c=s(e),u=1;u=a)return e;switch(e){case"%s":return String(n[r++]);case"%d":return Number(n[r++]);case"%j":try{return JSON.stringify(n[r++])}catch(e){return"[Circular]"}default:return e}}),c=n[r];r=3&&(n.depth=arguments[2]),arguments.length>=4&&(n.colors=arguments[3]),h(r)?n.showHidden=r:r&&t._extend(n,r),y(n.showHidden)&&(n.showHidden=!1),y(n.depth)&&(n.depth=2),y(n.colors)&&(n.colors=!1),y(n.customInspect)&&(n.customInspect=!0),n.colors&&(n.stylize=c),l(n,e,n.depth)}function c(e,t){var r=o.styles[t];return r?"["+o.colors[r][0]+"m"+e+"["+o.colors[r][1]+"m":e}function u(e,t){return e}function l(e,r,n){if(e.customInspect&&r&&k(r.inspect)&&r.inspect!==t.inspect&&(!r.constructor||r.constructor.prototype!==r)){var i=r.inspect(n,e);return b(i)||(i=l(e,i,n)),i}var a=function(e,t){if(y(t))return e.stylize("undefined","undefined");if(b(t)){var r="'"+JSON.stringify(t).replace(/^"|"$/g,"").replace(/'/g,"\\'").replace(/\\"/g,'"')+"'";return e.stylize(r,"string")}if(g(t))return e.stylize(""+t,"number");if(h(t))return e.stylize(""+t,"boolean");if(m(t))return e.stylize("null","null")}(e,r);if(a)return a;var s=Object.keys(r),o=function(e){var t={};return e.forEach(function(e,r){t[e]=!0}),t}(s);if(e.showHidden&&(s=Object.getOwnPropertyNames(r)),I(r)&&(s.indexOf("message")>=0||s.indexOf("description")>=0))return d(r);if(0===s.length){if(k(r)){var c=r.name?": "+r.name:"";return e.stylize("[Function"+c+"]","special")}if(w(r))return e.stylize(RegExp.prototype.toString.call(r),"regexp");if(v(r))return e.stylize(Date.prototype.toString.call(r),"date");if(I(r))return d(r)}var u,T="",S=!1,C=["{","}"];(p(r)&&(S=!0,C=["[","]"]),k(r))&&(T=" [Function"+(r.name?": "+r.name:"")+"]");return w(r)&&(T=" "+RegExp.prototype.toString.call(r)),v(r)&&(T=" "+Date.prototype.toUTCString.call(r)),I(r)&&(T=" "+d(r)),0!==s.length||S&&0!=r.length?n<0?w(r)?e.stylize(RegExp.prototype.toString.call(r),"regexp"):e.stylize("[Object]","special"):(e.seen.push(r),u=S?function(e,t,r,n,i){for(var a=[],s=0,o=t.length;s=0&&0,e+t.replace(/\u001b\[\d\d?m/g,"").length+1},0)>60)return r[0]+(""===t?"":t+"\n ")+" "+e.join(",\n ")+" "+r[1];return r[0]+t+" "+e.join(", ")+" "+r[1]}(u,T,C)):C[0]+T+C[1]}function d(e){return"["+Error.prototype.toString.call(e)+"]"}function f(e,t,r,n,i,a){var s,o,c;if((c=Object.getOwnPropertyDescriptor(t,i)||{value:t[i]}).get?o=c.set?e.stylize("[Getter/Setter]","special"):e.stylize("[Getter]","special"):c.set&&(o=e.stylize("[Setter]","special")),F(n,i)||(s="["+i+"]"),o||(e.seen.indexOf(c.value)<0?(o=m(r)?l(e,c.value,null):l(e,c.value,r-1)).indexOf("\n")>-1&&(o=a?o.split("\n").map(function(e){return" "+e}).join("\n").substr(2):"\n"+o.split("\n").map(function(e){return" "+e}).join("\n")):o=e.stylize("[Circular]","special")),y(s)){if(a&&i.match(/^\d+$/))return o;(s=JSON.stringify(""+i)).match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)?(s=s.substr(1,s.length-2),s=e.stylize(s,"name")):(s=s.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'"),s=e.stylize(s,"string"))}return s+": "+o}function p(e){return Array.isArray(e)}function h(e){return"boolean"==typeof e}function m(e){return null===e}function g(e){return"number"==typeof e}function b(e){return"string"==typeof e}function y(e){return void 0===e}function w(e){return T(e)&&"[object RegExp]"===S(e)}function T(e){return"object"==typeof e&&null!==e}function v(e){return T(e)&&"[object Date]"===S(e)}function I(e){return T(e)&&("[object Error]"===S(e)||e instanceof Error)}function k(e){return"function"==typeof e}function S(e){return Object.prototype.toString.call(e)}function C(e){return e<10?"0"+e.toString(10):e.toString(10)}t.debuglog=function(r){if(y(a)&&(a=e.env.NODE_DEBUG||""),r=r.toUpperCase(),!s[r])if(new RegExp("\\b"+r+"\\b","i").test(a)){var n=e.pid;s[r]=function(){var e=t.format.apply(t,arguments);console.error("%s %d: %s",r,n,e)}}else s[r]=function(){};return s[r]},t.inspect=o,o.colors={bold:[1,22],italic:[3,23],underline:[4,24],inverse:[7,27],white:[37,39],grey:[90,39],black:[30,39],blue:[34,39],cyan:[36,39],green:[32,39],magenta:[35,39],red:[31,39],yellow:[33,39]},o.styles={special:"cyan",number:"yellow",boolean:"yellow",undefined:"grey",null:"bold",string:"green",date:"magenta",regexp:"red"},t.isArray=p,t.isBoolean=h,t.isNull=m,t.isNullOrUndefined=function(e){return null==e},t.isNumber=g,t.isString=b,t.isSymbol=function(e){return"symbol"==typeof e},t.isUndefined=y,t.isRegExp=w,t.isObject=T,t.isDate=v,t.isError=I,t.isFunction=k,t.isPrimitive=function(e){return null===e||"boolean"==typeof e||"number"==typeof e||"string"==typeof e||"symbol"==typeof e||void 0===e},t.isBuffer=r(198);var E=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function _(){var e=new Date,t=[C(e.getHours()),C(e.getMinutes()),C(e.getSeconds())].join(":");return[e.getDate(),E[e.getMonth()],t].join(" ")}function F(e,t){return Object.prototype.hasOwnProperty.call(e,t)}t.log=function(){console.log("%s - %s",_(),t.format.apply(t,arguments))},t.inherits=r(199),t._extend=function(e,t){if(!t||!T(t))return e;for(var r=Object.keys(t),n=r.length;n--;)e[r[n]]=t[r[n]];return e};var x="undefined"!=typeof Symbol?Symbol("util.promisify.custom"):void 0;function A(e,t){if(!e){var r=new Error("Promise was rejected with a falsy value");r.reason=e,e=r}return t(e)}t.promisify=function(e){if("function"!=typeof e)throw new TypeError('The "original" argument must be of type Function');if(x&&e[x]){var t;if("function"!=typeof(t=e[x]))throw new TypeError('The "util.promisify.custom" argument must be of type Function');return Object.defineProperty(t,x,{value:t,enumerable:!1,writable:!1,configurable:!0}),t}function t(){for(var t,r,n=new Promise(function(e,n){t=e,r=n}),i=[],a=0;a{"%%"!==e&&(n++,"%c"===e&&(i=n))}),t.splice(i,0,r)},t.save=function(e){try{e?t.storage.setItem("debug",e):t.storage.removeItem("debug")}catch(e){}},t.load=function(){let e;try{e=t.storage.getItem("debug")}catch(e){}!e&&void 0!==n&&"env"in n&&(e=n.env.DEBUG);return e},t.useColors=function(){if("undefined"!=typeof window&&window.process&&("renderer"===window.process.type||window.process.__nwjs))return!0;if("undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))return!1;return"undefined"!=typeof document&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||"undefined"!=typeof window&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)},t.storage=function(){try{return localStorage}catch(e){}}(),t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"],e.exports=r(201)(t);const{formatters:i}=e.exports;i.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}}).call(this,r(9))},function(e,t,r){e.exports=function(e){function t(e){let t=0;for(let r=0;r{if("%%"===r)return r;o++;const a=n.formatters[i];if("function"==typeof a){const n=e[o];r=a.call(t,n),e.splice(o,1),o--}return r}),n.formatArgs.call(t,e),(t.log||n.log).apply(t,e)}return s.namespace=e,s.enabled=n.enabled(e),s.useColors=n.useColors(),s.color=t(e),s.destroy=i,s.extend=a,"function"==typeof n.init&&n.init(s),n.instances.push(s),s}function i(){const e=n.instances.indexOf(this);return-1!==e&&(n.instances.splice(e,1),!0)}function a(e,t){const r=n(this.namespace+(void 0===t?":":t)+e);return r.log=this.log,r}function s(e){return e.toString().substring(2,e.toString().length-2).replace(/\.\*\?$/,"*")}return n.debug=n,n.default=n,n.coerce=function(e){return e instanceof Error?e.stack||e.message:e},n.disable=function(){const e=[...n.names.map(s),...n.skips.map(s).map(e=>"-"+e)].join(",");return n.enable(""),e},n.enable=function(e){let t;n.save(e),n.names=[],n.skips=[];const r=("string"==typeof e?e:"").split(/[\s,]+/),i=r.length;for(t=0;t{n[t]=e[t]}),n.instances=[],n.names=[],n.skips=[],n.formatters={},n.selectColor=t,n.enable(n.load()),n}},function(e,t){var r=1e3,n=60*r,i=60*n,a=24*i,s=7*a,o=365.25*a;function c(e,t,r,n){var i=t>=1.5*r;return Math.round(e/r)+" "+n+(i?"s":"")}e.exports=function(e,t){t=t||{};var u=typeof e;if("string"===u&&e.length>0)return function(e){if((e=String(e)).length>100)return;var t=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(!t)return;var c=parseFloat(t[1]);switch((t[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return c*o;case"weeks":case"week":case"w":return c*s;case"days":case"day":case"d":return c*a;case"hours":case"hour":case"hrs":case"hr":case"h":return c*i;case"minutes":case"minute":case"mins":case"min":case"m":return c*n;case"seconds":case"second":case"secs":case"sec":case"s":return c*r;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return c;default:return}}(e);if("number"===u&&isFinite(e))return t.long?function(e){var t=Math.abs(e);if(t>=a)return c(e,t,a,"day");if(t>=i)return c(e,t,i,"hour");if(t>=n)return c(e,t,n,"minute");if(t>=r)return c(e,t,r,"second");return e+" ms"}(e):function(e){var t=Math.abs(e);if(t>=a)return Math.round(e/a)+"d";if(t>=i)return Math.round(e/i)+"h";if(t>=n)return Math.round(e/n)+"m";if(t>=r)return Math.round(e/r)+"s";return e+"ms"}(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(10);t.BufferTokenizer=class{constructor(e){this.buffer=e,this.position=0,this.fileSize=e.length}async readBuffer(e,t,r,n){return this.position=n||this.position,this.peekBuffer(e,t,r,this.position).then(e=>(this.position+=e,e))}async peekBuffer(e,t,r,i,a=!1){i=i||this.position,r||(r=e.length);const s=Math.min(this.buffer.length-i,r);if(!a&&s[...e].map(e=>e.charCodeAt(0));const r=(e,t,r)=>String.fromCharCode(...e.slice(t,r));t.readUInt64LE=(e,t=0)=>{let r=e[t],n=1,i=0;for(;++i<8;)n*=256,r+=e[t+i]*n;return r},t.tarHeaderChecksumMatches=e=>{if(e.length<512)return!1;let t=256,n=0;for(let r=0;r<148;r++){const i=e[r];t+=i,n+=128&i}for(let r=156;r<512;r++){const i=e[r];t+=i,n+=128&i}const i=parseInt(r(e,148,154),8);return i===t||i===t-(n<<1)},t.multiByteIndexOf=(t,r,n=0)=>{if(e&&e.isBuffer(t))return t.indexOf(e.from(r),n);const i=(e,t,r)=>{for(let n=1;n=0;){if(i(t,r,a))return a;a=t.indexOf(r[0],a+1)}return-1},t.uint8ArrayUtf8ByteString=r}).call(this,r(3).Buffer)},function(e,t,r){"use strict";e.exports={extensions:["jpg","png","apng","gif","webp","flif","cr2","orf","arw","dng","nef","rw2","raf","tif","bmp","jxr","psd","zip","tar","rar","gz","bz2","7z","dmg","mp4","mid","mkv","webm","mov","avi","wmv","mpg","mp2","mp3","m4a","oga","ogg","ogv","opus","flac","wav","spx","amr","pdf","epub","exe","swf","rtf","wasm","woff","woff2","eot","ttf","otf","ico","flv","ps","xz","sqlite","nes","crx","xpi","cab","deb","ar","rpm","Z","lz","msi","mxf","mts","blend","bpg","docx","pptx","xlsx","3gp","3g2","jp2","jpm","jpx","mj2","aif","qcp","odt","ods","odp","xml","mobi","heic","cur","ktx","ape","wv","wmv","wma","dcm","ics","glb","pcap","dsf","lnk","alias","voc","ac3","m4v","m4p","m4b","f4v","f4p","f4b","f4a"],mimeTypes:["image/jpeg","image/png","image/gif","image/webp","image/flif","image/x-canon-cr2","image/tiff","image/bmp","image/vnd.ms-photo","image/vnd.adobe.photoshop","application/epub+zip","application/x-xpinstall","application/vnd.oasis.opendocument.text","application/vnd.oasis.opendocument.spreadsheet","application/vnd.oasis.opendocument.presentation","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/vnd.openxmlformats-officedocument.presentationml.presentation","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","application/zip","application/x-tar","application/x-rar-compressed","application/gzip","application/x-bzip2","application/x-7z-compressed","application/x-apple-diskimage","video/mp4","audio/midi","video/x-matroska","video/webm","video/quicktime","video/vnd.avi","audio/vnd.wave","audio/qcelp","audio/x-ms-wma","video/x-ms-asf","application/vnd.ms-asf","video/mpeg","video/3gpp","audio/mpeg","audio/mp4","audio/opus","video/ogg","audio/ogg","application/ogg","audio/x-flac","audio/ape","audio/wavpack","audio/amr","application/pdf","application/x-msdownload","application/x-shockwave-flash","application/rtf","application/wasm","font/woff","font/woff2","application/vnd.ms-fontobject","font/ttf","font/otf","image/x-icon","video/x-flv","application/postscript","application/x-xz","application/x-sqlite3","application/x-nintendo-nes-rom","application/x-google-chrome-extension","application/vnd.ms-cab-compressed","application/x-deb","application/x-unix-archive","application/x-rpm","application/x-compress","application/x-lzip","application/x-msi","application/mxf","video/mp2t","application/x-blender","image/bpg","image/jp2","image/jpx","image/jpm","image/mj2","audio/aiff","application/xml","application/x-mobipocket-ebook","image/heif","image/heif-sequence","image/heic","image/heic-sequence","image/ktx","application/dicom","audio/x-musepack","text/calendar","model/gltf-binary","application/vnd.tcpdump.pcap","audio/x-dsf","application/x.ms.shortcut","application/x.apple.alias","audio/x-voc","audio/vnd.dolby.dd-raw","audio/x-m4a"]}},function(e,t,r){"use strict";var n=/; *([!#$%&'*+.^_`|~0-9A-Za-z-]+) *= *("(?:[\u000b\u0020\u0021\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\u000b\u0020-\u00ff])*"|[!#$%&'*+.^_`|~0-9A-Za-z-]+) */g,i=/^[\u000b\u0020-\u007e\u0080-\u00ff]+$/,a=/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/,s=/\\([\u000b\u0020-\u00ff])/g,o=/([\\"])/g,c=/^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;function u(e){var t=String(e);if(a.test(t))return t;if(t.length>0&&!i.test(t))throw new TypeError("invalid parameter value");return'"'+t.replace(o,"\\$1")+'"'}function l(e){this.parameters=Object.create(null),this.type=e}t.format=function(e){if(!e||"object"!=typeof e)throw new TypeError("argument obj is required");var t=e.parameters,r=e.type;if(!r||!c.test(r))throw new TypeError("invalid type");var n=r;if(t&&"object"==typeof t)for(var i,s=Object.keys(t).sort(),o=0;o{if("%%"===r)return r;o++;const a=n.formatters[i];if("function"==typeof a){const n=e[o];r=a.call(t,n),e.splice(o,1),o--}return r}),n.formatArgs.call(t,e),(t.log||n.log).apply(t,e)}return s.namespace=e,s.enabled=n.enabled(e),s.useColors=n.useColors(),s.color=t(e),s.destroy=i,s.extend=a,"function"==typeof n.init&&n.init(s),n.instances.push(s),s}function i(){const e=n.instances.indexOf(this);return-1!==e&&(n.instances.splice(e,1),!0)}function a(e,t){const r=n(this.namespace+(void 0===t?":":t)+e);return r.log=this.log,r}function s(e){return e.toString().substring(2,e.toString().length-2).replace(/\.\*\?$/,"*")}return n.debug=n,n.default=n,n.coerce=function(e){return e instanceof Error?e.stack||e.message:e},n.disable=function(){const e=[...n.names.map(s),...n.skips.map(s).map(e=>"-"+e)].join(",");return n.enable(""),e},n.enable=function(e){let t;n.save(e),n.names=[],n.skips=[];const r=("string"==typeof e?e:"").split(/[\s,]+/),i=r.length;for(t=0;t{n[t]=e[t]}),n.instances=[],n.names=[],n.skips=[],n.formatters={},n.selectColor=t,n.enable(n.load()),n}},function(e,t){var r=1e3,n=60*r,i=60*n,a=24*i,s=7*a,o=365.25*a;function c(e,t,r,n){var i=t>=1.5*r;return Math.round(e/r)+" "+n+(i?"s":"")}e.exports=function(e,t){t=t||{};var u=typeof e;if("string"===u&&e.length>0)return function(e){if((e=String(e)).length>100)return;var t=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(!t)return;var c=parseFloat(t[1]);switch((t[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return c*o;case"weeks":case"week":case"w":return c*s;case"days":case"day":case"d":return c*a;case"hours":case"hour":case"hrs":case"hr":case"h":return c*i;case"minutes":case"minute":case"mins":case"min":case"m":return c*n;case"seconds":case"second":case"secs":case"sec":case"s":return c*r;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return c;default:return}}(e);if("number"===u&&isFinite(e))return t.long?function(e){var t=Math.abs(e);if(t>=a)return c(e,t,a,"day");if(t>=i)return c(e,t,i,"hour");if(t>=n)return c(e,t,n,"minute");if(t>=r)return c(e,t,r,"second");return e+" ms"}(e):function(e){var t=Math.abs(e);if(t>=a)return Math.round(e/a)+"d";if(t>=i)return Math.round(e/i)+"h";if(t>=n)return Math.round(e/n)+"m";if(t>=r)return Math.round(e/r)+"s";return e+"ms"}(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(2),i=r(212),a=r(213),s=r(8),o=r(4),c=n("music-metadata:collector"),u=["APEv2","vorbis","ID3v2.4","ID3v2.3","ID3v2.2","exif","asf","iTunes","ID3v1"];function l(e){return e.length>2?e.slice(0,e.length-1).join(", ")+" & "+e[e.length-1]:e.join(" & ")}t.MetadataCollector=class{constructor(e){this.opts=e,this.format={tagTypes:[]},this.native={},this.common={track:{no:null,of:null},disk:{no:null,of:null}},this.commonOrigin={},this.originPriority={},this.tagMapper=new a.CombinedTagMapper;let t=1;for(const e of u)this.originPriority[e]=t++;this.originPriority.artificial=500,this.originPriority.id3v1=600}hasAny(){for(const e in this.native)return!0;return!1}setFormat(e,t){c(`format: ${e} = ${t}`),this.format[e]=t,this.opts.observer&&this.opts.observer({metadata:this,tag:{type:"format",id:e,value:t}})}addTag(e,t,r){c(`tag ${e}.${t} = ${r}`),this.native[e]||(this.format.tagTypes.push(e),this.native[e]=[]),this.native[e].push({id:t,value:r}),this.toCommon(e,t,r)}getNativeMetadata(){return{format:this.format,native:this.native}}postMap(e,t){switch(t.id){case"artist":if(this.commonOrigin.artist===this.originPriority[e])return this.postMap("artificial",{id:"artists",value:t.value});this.common.artists||this.setGenericTag("artificial",{id:"artists",value:t.value});break;case"artists":if(!(this.common.artist&&this.commonOrigin.artist!==this.originPriority.artificial||this.common.artists&&-1!==this.common.artists.indexOf(t.value))){const e={id:"artist",value:l((this.common.artists||[]).concat([t.value]))};this.setGenericTag("artificial",e)}break;case"genre":t.value=s.CommonTagMapper.parseGenre(t.value);break;case"picture":t.value.format=s.CommonTagMapper.fixPictureMimeType(t.value.format);break;case"totaltracks":return void(this.common.track.of=s.CommonTagMapper.toIntOrNull(t.value));case"totaldiscs":return void(this.common.disk.of=s.CommonTagMapper.toIntOrNull(t.value));case"track":case"disk":const r=this.common[t.id].of;return this.common[t.id]=s.CommonTagMapper.normalizeTrack(t.value),void(this.common[t.id].of=null!=r?r:this.common[t.id].of);case"year":case"originalyear":t.value=parseInt(t.value,10);break;case"date":const n=parseInt(t.value.substr(0,4),10);n&&!isNaN(n)&&(this.common.year=n);break;case"discogs_label_id":case"discogs_release_id":case"discogs_master_release_id":case"discogs_artist_id":case"discogs_votes":t.value="string"==typeof t.value?parseInt(t.value,10):t.value;break;case"replaygain_track_gain":case"replaygain_track_peak":t.value=o.toRatio(t.value);break;case"gapless":t.value="1"===t.value}this.setGenericTag(e,t)}toCommonMetadata(){return{format:this.format,native:this.opts.native?this.native:void 0,common:this.common}}toCommon(e,t,r){const n={id:t,value:r},i=this.tagMapper.mapTag(e,n);i&&this.postMap(e,i)}setGenericTag(e,t){c(`common.${t.id} = ${t.value}`);const r=this.commonOrigin[t.id]||1e3,n=this.originPriority[e];if(i.isSingleton(t.id)){if(!(n<=r))return c(`Ignore native tag (singleton): ${e}.${t.id} = ${t.value}`);this.common[t.id]=t.value,this.commonOrigin[t.id]=n}else if(n===r)i.isUnique(t.id)&&-1!==this.common[t.id].indexOf(t.value)?c(`Ignore duplicate value: ${e}.${t.id} = ${t.value}`):this.common[t.id].push(t.value);else{if(!(n{this.registerTagMapper(e)})}mapTag(e,t){if(this.tagMappers[e])return this.tagMappers[e].mapGenericTag(t);throw new Error("No generic tag mapper defined for tag-format: "+e)}registerTagMapper(e){for(const t of e.tagTypes)this.tagMappers[t]=e}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(8),i={title:"title",artist:"artist",album:"album",year:"year",comment:"comment",track:"track",genre:"genre"};t.ID3v1TagMapper=class extends n.CommonTagMapper{constructor(){super(["ID3v1"],i)}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});class n{static decode(e){let t="";for(const r in e)e.hasOwnProperty(r)&&(t+=n.codePointToString(n.singleByteDecoder(e[r])));return t}static inRange(e,t,r){return t<=e&&e<=r}static codePointToString(e){return e<=65535?String.fromCharCode(e):(e-=65536,String.fromCharCode(55296+(e>>10),56320+(1023&e)))}static singleByteDecoder(e){if(n.inRange(e,0,127))return e;const t=n.windows1252[e-128];if(null===t)throw Error("invaliding encoding");return t}}n.windows1252=[8364,129,8218,402,8222,8230,8224,8225,710,8240,352,8249,338,141,381,143,144,8216,8217,8220,8221,8226,8211,8212,732,8482,353,8250,339,157,382,376,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255],t.Windows1292Decoder=n},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(8),i=r(4),a={TIT2:"title",TPE1:"artist","TXXX:Artists":"artists",TPE2:"albumartist",TALB:"album",TDRV:"date",TORY:"originalyear",TPOS:"disk",TCON:"genre",APIC:"picture",TCOM:"composer","USLT:description":"lyrics",TSOA:"albumsort",TSOT:"titlesort",TOAL:"originalalbum",TSOP:"artistsort",TSO2:"albumartistsort",TSOC:"composersort",TEXT:"lyricist","TXXX:Writer":"writer",TPE3:"conductor",TPE4:"remixer","IPLS:arranger":"arranger","IPLS:engineer":"engineer","IPLS:producer":"producer","IPLS:DJ-mix":"djmixer","IPLS:mix":"mixer",TPUB:"label",TIT1:"grouping",TIT3:"subtitle",TRCK:"track",TCMP:"compilation",POPM:"rating",TBPM:"bpm",TMED:"media","TXXX:CATALOGNUMBER":"catalognumber","TXXX:MusicBrainz Album Status":"releasestatus","TXXX:MusicBrainz Album Type":"releasetype","TXXX:MusicBrainz Album Release Country":"releasecountry","TXXX:RELEASECOUNTRY":"releasecountry","TXXX:SCRIPT":"script",TLAN:"language",TCOP:"copyright",WCOP:"license",TENC:"encodedby",TSSE:"encodersettings","TXXX:BARCODE":"barcode",TSRC:"isrc","TXXX:ASIN":"asin","TXXX:originalyear":"originalyear","UFID:http://musicbrainz.org":"musicbrainz_recordingid","TXXX:MusicBrainz Release Track Id":"musicbrainz_trackid","TXXX:MusicBrainz Album Id":"musicbrainz_albumid","TXXX:MusicBrainz Artist Id":"musicbrainz_artistid","TXXX:MusicBrainz Album Artist Id":"musicbrainz_albumartistid","TXXX:MusicBrainz Release Group Id":"musicbrainz_releasegroupid","TXXX:MusicBrainz Work Id":"musicbrainz_workid","TXXX:MusicBrainz TRM Id":"musicbrainz_trmid","TXXX:MusicBrainz Disc Id":"musicbrainz_discid","TXXX:ACOUSTID_ID":"acoustid_id","TXXX:Acoustid Id":"acoustid_id","TXXX:Acoustid Fingerprint":"acoustid_fingerprint","TXXX:MusicIP PUID":"musicip_puid","TXXX:MusicMagic Fingerprint":"musicip_fingerprint",WOAR:"website",TDRC:"date",TYER:"year",TDOR:"originaldate","TIPL:arranger":"arranger","TIPL:engineer":"engineer","TIPL:producer":"producer","TIPL:DJ-mix":"djmixer","TIPL:mix":"mixer",TMOO:"mood",SYLT:"lyrics",TSST:"discsubtitle",TKEY:"key",COMM:"comment",TOPE:"originalartist","PRIV:AverageLevel":"averageLevel","PRIV:PeakLevel":"peakLevel","TXXX:DISCOGS_ARTIST_ID":"discogs_artist_id","TXXX:DISCOGS_ARTISTS":"artists","TXXX:DISCOGS_ARTIST_NAME":"artists","TXXX:DISCOGS_ALBUM_ARTISTS":"albumartist","TXXX:DISCOGS_CATALOG":"catalognumber","TXXX:DISCOGS_COUNTRY":"releasecountry","TXXX:DISCOGS_DATE":"originaldate","TXXX:DISCOGS_LABEL":"label","TXXX:DISCOGS_LABEL_ID":"discogs_label_id","TXXX:DISCOGS_MASTER_RELEASE_ID":"discogs_master_release_id","TXXX:DISCOGS_RATING":"discogs_rating","TXXX:DISCOGS_RELEASED":"date","TXXX:DISCOGS_RELEASE_ID":"discogs_release_id","TXXX:DISCOGS_VOTES":"discogs_votes","TXXX:CATALOGID":"catalognumber","TXXX:STYLE":"genre","TXXX:replaygain_track_peak":"replaygain_track_peak","TXXX:replaygain_track_gain":"replaygain_track_gain"};class s extends n.CommonTagMapper{static toRating(e){return{source:e.email,rating:e.rating>0?(e.rating-1)/254*n.CommonTagMapper.maxRatingScore:void 0}}constructor(){super(["ID3v2.3","ID3v2.4"],a)}postMap(e){switch(e.id){case"UFID":"http://musicbrainz.org"===e.value.owner_identifier&&(e.id+=":"+e.value.owner_identifier,e.value=i.default.decodeString(e.value.identifier,"iso-8859-1"));break;case"PRIV":switch(e.value.owner_identifier){case"AverageLevel":case"PeakValue":e.id+=":"+e.value.owner_identifier,e.value=4===e.value.data.length?e.value.data.readUInt32LE(0):null}break;case"COMM":e.value=e.value?e.value.text:null;break;case"POPM":e.value=s.toRating(e.value)}}}t.ID3v24TagMapper=s},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(8),i={Title:"title",Author:"artist","WM/AlbumArtist":"albumartist","WM/AlbumTitle":"album","WM/Year":"date","WM/OriginalReleaseTime":"originaldate","WM/OriginalReleaseYear":"originalyear",Description:"comment","WM/TrackNumber":"track","WM/PartOfSet":"disk","WM/Genre":"genre","WM/Composer":"composer","WM/Lyrics":"lyrics","WM/AlbumSortOrder":"albumsort","WM/TitleSortOrder":"titlesort","WM/ArtistSortOrder":"artistsort","WM/AlbumArtistSortOrder":"albumartistsort","WM/ComposerSortOrder":"composersort","WM/Writer":"lyricist","WM/Conductor":"conductor","WM/ModifiedBy":"remixer","WM/Engineer":"engineer","WM/Producer":"producer","WM/DJMixer":"djmixer","WM/Mixer":"mixer","WM/Publisher":"label","WM/ContentGroupDescription":"grouping","WM/SubTitle":"subtitle","WM/SetSubTitle":"discsubtitle","WM/IsCompilation":"compilation","WM/SharedUserRating":"rating","WM/BeatsPerMinute":"bpm","WM/Mood":"mood","WM/Media":"media","WM/CatalogNo":"catalognumber","MusicBrainz/Album Status":"releasestatus","MusicBrainz/Album Type":"releasetype","MusicBrainz/Album Release Country":"releasecountry","WM/Script":"script","WM/Language":"language",Copyright:"copyright",LICENSE:"license","WM/EncodedBy":"encodedby","WM/EncodingSettings":"encodersettings","WM/Barcode":"barcode","WM/ISRC":"isrc","MusicBrainz/Track Id":"musicbrainz_recordingid","MusicBrainz/Release Track Id":"musicbrainz_trackid","MusicBrainz/Album Id":"musicbrainz_albumid","MusicBrainz/Artist Id":"musicbrainz_artistid","MusicBrainz/Album Artist Id":"musicbrainz_albumartistid","MusicBrainz/Release Group Id":"musicbrainz_releasegroupid","MusicBrainz/Work Id":"musicbrainz_workid","MusicBrainz/TRM Id":"musicbrainz_trmid","MusicBrainz/Disc Id":"musicbrainz_discid","Acoustid/Id":"acoustid_id","Acoustid/Fingerprint":"acoustid_fingerprint","MusicIP/PUID":"musicip_puid","WM/ARTISTS":"artists","WM/InitialKey":"key",ASIN:"asin","WM/Work":"work","WM/AuthorURL":"website","WM/Picture":"picture"};class a extends n.CommonTagMapper{static toRating(e){return{rating:parseFloat(e+1)/5}}constructor(){super(["asf"],i)}postMap(e){switch(e.id){case"WM/SharedUserRating":const t=e.id.split(":");e.value=a.toRating(e.value),e.id=t[0]}}}t.AsfTagMapper=a},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(8);t.id3v22TagMap={TT2:"title",TP1:"artist",TP2:"albumartist",TAL:"album",TYE:"year",COM:"comment",TRK:"track",TPA:"disk",TCO:"genre",PIC:"picture",TCM:"composer",TOR:"originaldate",TOT:"work",TXT:"lyricist",TP3:"conductor",TPB:"label",TT1:"grouping",TT3:"subtitle",TLA:"language",TCR:"copyright",WCP:"license",TEN:"encodedby",TSS:"encodersettings",WAR:"website","COM:iTunPGAP":"gapless"};t.ID3v22TagMapper=class extends n.CommonTagMapper{constructor(){super(["ID3v2.2"],t.id3v22TagMap)}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(8),i={Title:"title",Artist:"artist",Artists:"artists","Album Artist":"albumartist",Album:"album",Year:"date",Originalyear:"originalyear",Originaldate:"originaldate",Comment:"comment",Track:"track",Disc:"disk",DISCNUMBER:"disk",Genre:"genre","Cover Art (Front)":"picture","Cover Art (Back)":"picture",Composer:"composer",Lyrics:"lyrics",ALBUMSORT:"albumsort",TITLESORT:"titlesort",WORK:"work",ARTISTSORT:"artistsort",ALBUMARTISTSORT:"albumartistsort",COMPOSERSORT:"composersort",Lyricist:"lyricist",Writer:"writer",Conductor:"conductor",MixArtist:"remixer",Arranger:"arranger",Engineer:"engineer",Producer:"producer",DJMixer:"djmixer",Mixer:"mixer",Label:"label",Grouping:"grouping",Subtitle:"subtitle",DiscSubtitle:"discsubtitle",Compilation:"compilation",BPM:"bpm",Mood:"mood",Media:"media",CatalogNumber:"catalognumber",MUSICBRAINZ_ALBUMSTATUS:"releasestatus",MUSICBRAINZ_ALBUMTYPE:"releasetype",RELEASECOUNTRY:"releasecountry",Script:"script",Language:"language",Copyright:"copyright",LICENSE:"license",EncodedBy:"encodedby",EncoderSettings:"encodersettings",Barcode:"barcode",ISRC:"isrc",ASIN:"asin",musicbrainz_trackid:"musicbrainz_recordingid",musicbrainz_releasetrackid:"musicbrainz_trackid",MUSICBRAINZ_ALBUMID:"musicbrainz_albumid",MUSICBRAINZ_ARTISTID:"musicbrainz_artistid",MUSICBRAINZ_ALBUMARTISTID:"musicbrainz_albumartistid",MUSICBRAINZ_RELEASEGROUPID:"musicbrainz_releasegroupid",MUSICBRAINZ_WORKID:"musicbrainz_workid",MUSICBRAINZ_TRMID:"musicbrainz_trmid",MUSICBRAINZ_DISCID:"musicbrainz_discid",Acoustid_Id:"acoustid_id",ACOUSTID_FINGERPRINT:"acoustid_fingerprint",MUSICIP_PUID:"musicip_puid",Weblink:"website"};t.APEv2TagMapper=class extends n.CommonTagMapper{constructor(){const e={};for(const t in i)e[t.toUpperCase()]=i[t];super(["APEv2"],e)}getCommonName(e){return this.tagMap[e.toUpperCase()]}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(8),i={"©nam":"title","©ART":"artist",aART:"albumartist","----:com.apple.iTunes:Band":"albumartist","©alb":"album","©day":"date","©cmt":"comment",trkn:"track",disk:"disk","©gen":"genre",covr:"picture","©wrt":"composer","©lyr":"lyrics",soal:"albumsort",sonm:"titlesort",soar:"artistsort",soaa:"albumartistsort",soco:"composersort","----:com.apple.iTunes:LYRICIST":"lyricist","----:com.apple.iTunes:CONDUCTOR":"conductor","----:com.apple.iTunes:REMIXER":"remixer","----:com.apple.iTunes:ENGINEER":"engineer","----:com.apple.iTunes:PRODUCER":"producer","----:com.apple.iTunes:DJMIXER":"djmixer","----:com.apple.iTunes:MIXER":"mixer","----:com.apple.iTunes:LABEL":"label","©grp":"grouping","----:com.apple.iTunes:SUBTITLE":"subtitle","----:com.apple.iTunes:DISCSUBTITLE":"discsubtitle",cpil:"compilation",tmpo:"bpm","----:com.apple.iTunes:MOOD":"mood","----:com.apple.iTunes:MEDIA":"media","----:com.apple.iTunes:CATALOGNUMBER":"catalognumber",tvsh:"tvShow",tvsn:"tvSeason",tves:"tvEpisode",sosn:"tvShowSort",tven:"tvEpisodeId",tvnn:"tvNetwork",pcst:"podcast",purl:"podcasturl","----:com.apple.iTunes:MusicBrainz Album Status":"releasestatus","----:com.apple.iTunes:MusicBrainz Album Type":"releasetype","----:com.apple.iTunes:MusicBrainz Album Release Country":"releasecountry","----:com.apple.iTunes:SCRIPT":"script","----:com.apple.iTunes:LANGUAGE":"language",cprt:"copyright","----:com.apple.iTunes:LICENSE":"license","©too":"encodedby",pgap:"gapless","----:com.apple.iTunes:BARCODE":"barcode","----:com.apple.iTunes:ISRC":"isrc","----:com.apple.iTunes:ASIN":"asin","----:com.apple.iTunes:NOTES":"comment","----:com.apple.iTunes:MusicBrainz Track Id":"musicbrainz_recordingid","----:com.apple.iTunes:MusicBrainz Release Track Id":"musicbrainz_trackid","----:com.apple.iTunes:MusicBrainz Album Id":"musicbrainz_albumid","----:com.apple.iTunes:MusicBrainz Artist Id":"musicbrainz_artistid","----:com.apple.iTunes:MusicBrainz Album Artist Id":"musicbrainz_albumartistid","----:com.apple.iTunes:MusicBrainz Release Group Id":"musicbrainz_releasegroupid","----:com.apple.iTunes:MusicBrainz Work Id":"musicbrainz_workid","----:com.apple.iTunes:MusicBrainz TRM Id":"musicbrainz_trmid","----:com.apple.iTunes:MusicBrainz Disc Id":"musicbrainz_discid","----:com.apple.iTunes:Acoustid Id":"acoustid_id","----:com.apple.iTunes:Acoustid Fingerprint":"acoustid_fingerprint","----:com.apple.iTunes:MusicIP PUID":"musicip_puid","----:com.apple.iTunes:fingerprint":"musicip_fingerprint",gnre:"genre","----:com.apple.iTunes:ALBUMARTISTSORT":"albumartistsort","----:com.apple.iTunes:ARTISTS":"artists","----:com.apple.iTunes:ORIGINALDATE":"originaldate","----:com.apple.iTunes:ORIGINALYEAR":"originalyear",desc:"description",ldes:"description"};t.tagType="iTunes";t.MP4TagMapper=class extends n.CommonTagMapper{constructor(){super([t.tagType],i)}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(8),i={TITLE:"title",ARTIST:"artist",ARTISTS:"artists",ALBUMARTIST:"albumartist",ALBUM:"album",DATE:"date",ORIGINALDATE:"originaldate",ORIGINALYEAR:"originalyear",COMMENT:"comment",TRACKNUMBER:"track",DISCNUMBER:"disk",GENRE:"genre",METADATA_BLOCK_PICTURE:"picture",COMPOSER:"composer",LYRICS:"lyrics",ALBUMSORT:"albumsort",TITLESORT:"titlesort",WORK:"work",ARTISTSORT:"artistsort",ALBUMARTISTSORT:"albumartistsort",COMPOSERSORT:"composersort",LYRICIST:"lyricist",WRITER:"writer",CONDUCTOR:"conductor",REMIXER:"remixer",ARRANGER:"arranger",ENGINEER:"engineer",PRODUCER:"producer",DJMIXER:"djmixer",MIXER:"mixer",LABEL:"label",GROUPING:"grouping",SUBTITLE:"subtitle",DISCSUBTITLE:"discsubtitle",TRACKTOTAL:"totaltracks",DISCTOTAL:"totaldiscs",COMPILATION:"compilation",RATING:"rating",BPM:"bpm",MOOD:"mood",MEDIA:"media",CATALOGNUMBER:"catalognumber",RELEASESTATUS:"releasestatus",RELEASETYPE:"releasetype",RELEASECOUNTRY:"releasecountry",SCRIPT:"script",LANGUAGE:"language",COPYRIGHT:"copyright",LICENSE:"license",ENCODEDBY:"encodedby",ENCODERSETTINGS:"encodersettings",BARCODE:"barcode",ISRC:"isrc",ASIN:"asin",MUSICBRAINZ_TRACKID:"musicbrainz_recordingid",MUSICBRAINZ_RELEASETRACKID:"musicbrainz_trackid",MUSICBRAINZ_ALBUMID:"musicbrainz_albumid",MUSICBRAINZ_ARTISTID:"musicbrainz_artistid",MUSICBRAINZ_ALBUMARTISTID:"musicbrainz_albumartistid",MUSICBRAINZ_RELEASEGROUPID:"musicbrainz_releasegroupid",MUSICBRAINZ_WORKID:"musicbrainz_workid",MUSICBRAINZ_TRMID:"musicbrainz_trmid",MUSICBRAINZ_DISCID:"musicbrainz_discid",ACOUSTID_ID:"acoustid_id",ACOUSTID_ID_FINGERPRINT:"acoustid_fingerprint",MUSICIP_PUID:"musicip_puid",WEBSITE:"website",NOTES:"notes",TOTALTRACKS:"totaltracks",TOTALDISCS:"totaldiscs",DISCOGS_ARTIST_ID:"discogs_artist_id",DISCOGS_ARTISTS:"artists",DISCOGS_ARTIST_NAME:"artists",DISCOGS_ALBUM_ARTISTS:"albumartist",DISCOGS_CATALOG:"catalognumber",DISCOGS_COUNTRY:"releasecountry",DISCOGS_DATE:"originaldate",DISCOGS_LABEL:"label",DISCOGS_LABEL_ID:"discogs_label_id",DISCOGS_MASTER_RELEASE_ID:"discogs_master_release_id",DISCOGS_RATING:"discogs_rating",DISCOGS_RELEASED:"date",DISCOGS_RELEASE_ID:"discogs_release_id",DISCOGS_VOTES:"discogs_votes",CATALOGID:"catalognumber",STYLE:"genre",REPLAYGAIN_TRACK_GAIN:"replaygain_track_gain",REPLAYGAIN_TRACK_PEAK:"replaygain_track_peak"};class a extends n.CommonTagMapper{static toRating(e,t){return{source:e?e.toLowerCase():e,rating:parseFloat(t)*n.CommonTagMapper.maxRatingScore}}constructor(){super(["vorbis"],i)}postMap(e){if(0===e.id.indexOf("RATING:")){const t=e.id.split(":");e.value=a.toRating(t[1],e.value),e.id=t[0]}}}t.VorbisTagMapper=a},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(8);t.riffInfoTagMap={IART:"artist",ICRD:"date",INAM:"title",TITL:"title",IPRD:"album",ITRK:"track",COMM:"comment",ICMT:"comment",ICNT:"releasecountry",GNRE:"genre",IWRI:"writer",RATE:"rating",YEAR:"year",ISFT:"encodedby",CODE:"encodedby",TURL:"website",IGNR:"genre",IENG:"engineer",ITCH:"technician",IMED:"media",IRPD:"album"};t.RiffInfoTagMapper=class extends n.CommonTagMapper{constructor(){super(["exif"],t.riffInfoTagMap)}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(2),a=r(10),s=r(21),o=r(17),c=r(5),u=r(7),l=r(225),d=r(226),f=r(26),p=i("music-metadata:parser:aiff");t.AIFFParser=class extends u.BasicParser{async parse(){if("FORM"!==(await this.tokenizer.readToken(d.Header)).chunkID)throw new Error("Invalid Chunk-ID, expected 'FORM'");const e=await this.tokenizer.readToken(c.FourCcToken);switch(e){case"AIFF":this.metadata.setFormat("container",e),this.isCompressed=!1;break;case"AIFC":this.metadata.setFormat("container","AIFF-C"),this.isCompressed=!0;break;default:throw Error("Unsupported AIFF type: "+e)}this.metadata.setFormat("lossless",!this.isCompressed);try{for(;;){const e=await this.tokenizer.readToken(d.Header);p(`Chunk id=${e.chunkID}`);const t=2*Math.round(e.chunkSize/2),r=await this.readData(e);await this.tokenizer.ignore(t-r)}}catch(e){if(e.message!==a.endOfFile)throw e}}async readData(e){switch(e.chunkID){case"COMM":const t=await this.tokenizer.readToken(new l.Common(e,this.isCompressed));return this.metadata.setFormat("bitsPerSample",t.sampleSize),this.metadata.setFormat("sampleRate",t.sampleRate),this.metadata.setFormat("numberOfChannels",t.numChannels),this.metadata.setFormat("numberOfSamples",t.numSampleFrames),this.metadata.setFormat("duration",t.numSampleFrames/t.sampleRate),this.metadata.setFormat("codec",t.compressionName),e.chunkSize;case"ID3 ":const r=await this.tokenizer.readToken(new n.BufferType(e.chunkSize)),i=new f.ID3Stream(r),a=s.fromStream(i);return await(new o.ID3v2Parser).parse(this.metadata,a,this.options),e.chunkSize;case"SSND":return this.metadata.format.duration&&this.metadata.setFormat("bitrate",8*e.chunkSize/this.metadata.format.duration),0;default:return 0}}}},function(e,t,r){"use strict";(function(e){Object.defineProperty(t,"__esModule",{value:!0});const n=r(2),i=r(1),a=r(4),s=r(18),o=n("music-metadata:id3v2:frame-parser"),c="iso-8859-1";class u{static readData(t,r,n,l){const{encoding:d,bom:f}=s.TextEncodingToken.get(t,0),p=t.length;let h=0,m=[];const g=u.getNullTerminatorLength(d);let b;const y={};switch(o(`Parsing tag type=${r}, encoding=${d}, bom=${f}`),"TXXX"!==r&&"T"===r[0]?"T*":r){case"T*":case"IPLS":const w=a.default.decodeString(t.slice(1),d).replace(/\x00+$/,"");switch(r){case"TMCL":case"TIPL":case"IPLS":m=u.splitValue(4,w),m=u.functionList(m);break;case"TRK":case"TRCK":case"TPOS":m=w;break;case"TCOM":case"TEXT":case"TOLY":case"TOPE":case"TPE1":case"TSRC":m=u.splitValue(n,w);break;default:m=n>=4?u.splitValue(n,w):[w]}break;case"TXXX":m={description:(m=u.readIdentifierAndData(t,h+1,p,d)).id,text:u.splitValue(n,a.default.decodeString(m.data,d).replace(/\x00+$/,""))};break;case"PIC":case"APIC":if(l){const r={};switch(h+=1,n){case 2:r.format=a.default.decodeString(t.slice(h,h+3),d),h+=3;break;case 3:case 4:b=a.default.findZero(t,h,p,c),r.format=a.default.decodeString(t.slice(h,b),c),h=b+1;break;default:throw new Error("Warning: unexpected major versionIndex: "+n)}r.format=u.fixPictureMimeType(r.format),r.type=s.AttachedPictureType[t[h]],h+=1,b=a.default.findZero(t,h,p,d),r.description=a.default.decodeString(t.slice(h,b),d),h=b+g,r.data=e.from(t.slice(h,p)),m=r}break;case"CNT":case"PCNT":m=i.UINT32_BE.get(t,0);break;case"SYLT":for(h+=7,m=[];h=5?t.readUInt32BE(h+1):void 0};break;case"GEOB":{b=a.default.findZero(t,h+1,p,d);const e=a.default.decodeString(t.slice(h+1,b),c);h=b+1,b=a.default.findZero(t,h,p-h,d);const r=a.default.decodeString(t.slice(h+1,b),c);h=b+1,b=a.default.findZero(t,h,p-h,d),m={type:e,filename:r,description:a.default.decodeString(t.slice(h+1,b),c),data:t.slice(h+1,p)};break}case"WCOM":case"WCOP":case"WOAF":case"WOAR":case"WOAS":case"WORS":case"WPAY":case"WPUB":m=a.default.decodeString(t.slice(h,b),d);break;case"WXXX":{b=a.default.findZero(t,h+1,p,d);const e=a.default.decodeString(t.slice(h+1,b),c);h=b+1,m={description:e,url:a.default.decodeString(t.slice(h,p-h),d)};break}case"MCDI":m=t.slice(0,p);break;default:o("Warning: unsupported id3v2-tag-type: "+r)}return m}static fixPictureMimeType(e){switch(e=e.toLocaleLowerCase()){case"jpg":return"image/jpeg";case"png":return"image/png"}return e}static functionList(e){const t={};for(let r=0;r+1=4?/\x00/g:/\//g);return u.trimArray(r)}static trimArray(e){for(let t=0;t=r,`COMMON CHUNK size should always be at least ${r}`),this.len=e.chunkSize}get(e,t){const r=e.readUInt16BE(t+8)-16398,i=e.readUInt16BE(t+8+2),s={numChannels:e.readUInt16BE(t),numSampleFrames:e.readUInt32BE(t+2),sampleSize:e.readUInt16BE(t+6),sampleRate:r<0?i>>Math.abs(r):i<22){const r=e.readInt8(t+22);if(23+r+(r+1)%2!==this.len)throw new Error("Illegal pstring length");s.compressionName=new n.StringType(r,"binary").get(e,t+23)}}else s.compressionName="PCM";return s}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(5);t.Header={len:8,get:(e,t)=>({chunkID:n.FourCcToken.get(e,t),chunkSize:e.readUInt32BE(t+4)})}},function(e,t){},function(e,t,r){"use strict";var n=r(29).Buffer,i=r(229);e.exports=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.head=null,this.tail=null,this.length=0}return e.prototype.push=function(e){var t={data:e,next:null};this.length>0?this.tail.next=t:this.head=t,this.tail=t,++this.length},e.prototype.unshift=function(e){var t={data:e,next:this.head};0===this.length&&(this.tail=t),this.head=t,++this.length},e.prototype.shift=function(){if(0!==this.length){var e=this.head.data;return 1===this.length?this.head=this.tail=null:this.head=this.head.next,--this.length,e}},e.prototype.clear=function(){this.head=this.tail=null,this.length=0},e.prototype.join=function(e){if(0===this.length)return"";for(var t=this.head,r=""+t.data;t=t.next;)r+=e+t.data;return r},e.prototype.concat=function(e){if(0===this.length)return n.alloc(0);if(1===this.length)return this.head.data;for(var t,r,i,a=n.allocUnsafe(e>>>0),s=this.head,o=0;s;)t=s.data,r=a,i=o,t.copy(r,i),o+=s.data.length,s=s.next;return a},e}(),i&&i.inspect&&i.inspect.custom&&(e.exports.prototype[i.inspect.custom]=function(){var e=i.inspect({length:this.length});return this.constructor.name+" "+e})},function(e,t){},function(e,t,r){(function(e){var n=void 0!==e&&e||"undefined"!=typeof self&&self||window,i=Function.prototype.apply;function a(e,t){this._id=e,this._clearFn=t}t.setTimeout=function(){return new a(i.call(setTimeout,n,arguments),clearTimeout)},t.setInterval=function(){return new a(i.call(setInterval,n,arguments),clearInterval)},t.clearTimeout=t.clearInterval=function(e){e&&e.close()},a.prototype.unref=a.prototype.ref=function(){},a.prototype.close=function(){this._clearFn.call(n,this._id)},t.enroll=function(e,t){clearTimeout(e._idleTimeoutId),e._idleTimeout=t},t.unenroll=function(e){clearTimeout(e._idleTimeoutId),e._idleTimeout=-1},t._unrefActive=t.active=function(e){clearTimeout(e._idleTimeoutId);var t=e._idleTimeout;t>=0&&(e._idleTimeoutId=setTimeout(function(){e._onTimeout&&e._onTimeout()},t))},r(231),t.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,t.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(this,r(12))},function(e,t,r){(function(e,t){!function(e,r){"use strict";if(!e.setImmediate){var n,i,a,s,o,c=1,u={},l=!1,d=e.document,f=Object.getPrototypeOf&&Object.getPrototypeOf(e);f=f&&f.setTimeout?f:e,"[object process]"==={}.toString.call(e.process)?n=function(e){t.nextTick(function(){h(e)})}:!function(){if(e.postMessage&&!e.importScripts){var t=!0,r=e.onmessage;return e.onmessage=function(){t=!1},e.postMessage("","*"),e.onmessage=r,t}}()?e.MessageChannel?((a=new MessageChannel).port1.onmessage=function(e){h(e.data)},n=function(e){a.port2.postMessage(e)}):d&&"onreadystatechange"in d.createElement("script")?(i=d.documentElement,n=function(e){var t=d.createElement("script");t.onreadystatechange=function(){h(e),t.onreadystatechange=null,i.removeChild(t),t=null},i.appendChild(t)}):n=function(e){setTimeout(h,0,e)}:(s="setImmediate$"+Math.random()+"$",o=function(t){t.source===e&&"string"==typeof t.data&&0===t.data.indexOf(s)&&h(+t.data.slice(s.length))},e.addEventListener?e.addEventListener("message",o,!1):e.attachEvent("onmessage",o),n=function(t){e.postMessage(s+t,"*")}),f.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),r=0;r>1}}function s(e,t){return 0!=(e&1<({ID:i.FourCcToken.get(e,t),version:n.UINT32_LE.get(e,t+4)/1e3,descriptorBytes:n.UINT32_LE.get(e,t+8),headerBytes:n.UINT32_LE.get(e,t+12),seekTableBytes:n.UINT32_LE.get(e,t+16),headerDataBytes:n.UINT32_LE.get(e,t+20),apeFrameDataBytes:n.UINT32_LE.get(e,t+24),apeFrameDataBytesHigh:n.UINT32_LE.get(e,t+28),terminatingDataBytes:n.UINT32_LE.get(e,t+32),fileMD5:new n.BufferType(16).get(e,t+36)})},t.Header={len:24,get:(e,t)=>({compressionLevel:n.UINT16_LE.get(e,t),formatFlags:n.UINT16_LE.get(e,t+2),blocksPerFrame:n.UINT32_LE.get(e,t+4),finalFrameBlocks:n.UINT32_LE.get(e,t+8),totalFrames:n.UINT32_LE.get(e,t+12),bitsPerSample:n.UINT16_LE.get(e,t+16),channel:n.UINT16_LE.get(e,t+18),sampleRate:n.UINT32_LE.get(e,t+20)})},t.TagFooter={len:32,get:(e,t)=>({ID:new n.StringType(8,"ascii").get(e,t),version:n.UINT32_LE.get(e,t+8),size:n.UINT32_LE.get(e,t+12),fields:n.UINT32_LE.get(e,t+16),flags:a(n.UINT32_LE.get(e,t+20))})},t.TagItemHeader={len:8,get:(e,t)=>({size:n.UINT32_LE.get(e,t),flags:a(n.UINT32_LE.get(e,t+4))})},t.TagField=e=>new n.BufferType(e.size-t.TagFooter.len),t.parseTagFlags=a,t.isBitSet=s},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(42),i=r(241),a=r(2),s=r(7),o=a("music-metadata:parser:ASF"),c="asf";t.AsfParser=class extends s.BasicParser{async parse(){const e=await this.tokenizer.readToken(i.TopLevelHeaderObjectToken);if(!e.objectId.equals(n.default.HeaderObject))throw new Error("expected asf header; but was not found; got: "+e.objectId.str);try{await this.parseObjectHeader(e.numberOfHeaderObjects)}catch(e){o("Error while parsing ASF: %s",e)}}async parseObjectHeader(e){let t;do{const e=await this.tokenizer.readToken(i.HeaderObjectToken);switch(o("header GUID=%s",e.objectId.str),e.objectId.str){case i.FilePropertiesObject.guid.str:const r=await this.tokenizer.readToken(new i.FilePropertiesObject(e));this.metadata.setFormat("duration",r.playDuration/1e7),this.metadata.setFormat("bitrate",r.maximumBitrate);break;case i.StreamPropertiesObject.guid.str:const a=await this.tokenizer.readToken(new i.StreamPropertiesObject(e));this.metadata.setFormat("container","ASF/"+a.streamType);break;case i.HeaderExtensionObject.guid.str:const s=await this.tokenizer.readToken(new i.HeaderExtensionObject);await this.parseExtensionObject(s.extensionDataSize);break;case i.ContentDescriptionObjectState.guid.str:t=await this.tokenizer.readToken(new i.ContentDescriptionObjectState(e)),this.addTags(t);break;case i.ExtendedContentDescriptionObjectState.guid.str:t=await this.tokenizer.readToken(new i.ExtendedContentDescriptionObjectState(e)),this.addTags(t);break;case n.default.CodecListObject.str:case n.default.StreamBitratePropertiesObject.str:await this.tokenizer.ignore(e.objectSize-i.HeaderObjectToken.len);break;case n.default.PaddingObject.str:o("Padding: %s bytes",e.objectSize-i.HeaderObjectToken.len),await this.tokenizer.ignore(e.objectSize-i.HeaderObjectToken.len);break;default:this.warnings.push("Ignore ASF-Object-GUID: "+e.objectId.str),o("Ignore ASF-Object-GUID: %s",e.objectId.str),await this.tokenizer.readToken(new i.IgnoreObjectState(e))}}while(--e)}addTags(e){e.forEach(e=>{this.metadata.addTag(c,e.id,e.value)})}async parseExtensionObject(e){do{const t=await this.tokenizer.readToken(i.HeaderObjectToken);switch(t.objectId.str){case i.ExtendedStreamPropertiesObjectState.guid.str:await this.tokenizer.readToken(new i.ExtendedStreamPropertiesObjectState(t));break;case i.MetadataObjectState.guid.str:const e=await this.tokenizer.readToken(new i.MetadataObjectState(t));this.addTags(e);break;case i.MetadataLibraryObjectState.guid.str:const r=await this.tokenizer.readToken(new i.MetadataLibraryObjectState(t));this.addTags(r);break;case n.default.PaddingObject.str:await this.tokenizer.ignore(t.objectSize-i.HeaderObjectToken.len);break;case n.default.CompatibilityObject.str:this.tokenizer.ignore(t.objectSize-i.HeaderObjectToken.len);break;case n.default.ASF_Index_Placeholder_Object.str:await this.tokenizer.ignore(t.objectSize-i.HeaderObjectToken.len);break;default:this.warnings.push("Ignore ASF-Object-GUID: "+t.objectId.str),await this.tokenizer.readToken(new i.IgnoreObjectState(t))}e-=t.objectSize}while(e>0)}}},function(e,t,r){"use strict";(function(e){Object.defineProperty(t,"__esModule",{value:!0});const n=r(4),i=r(1),a=r(42),s=r(242),o=r(18);!function(e){e[e.UnicodeString=0]="UnicodeString",e[e.ByteArray=1]="ByteArray",e[e.Bool=2]="Bool",e[e.DWord=3]="DWord",e[e.QWord=4]="QWord",e[e.Word=5]="Word"}(t.DataType||(t.DataType={})),t.TopLevelHeaderObjectToken={len:30,get:(e,t)=>({objectId:a.default.fromBin(new i.BufferType(16).get(e,t)),objectSize:i.UINT64_LE.get(e,t+16),numberOfHeaderObjects:i.UINT32_LE.get(e,t+24)})},t.HeaderObjectToken={len:24,get:(e,t)=>({objectId:a.default.fromBin(new i.BufferType(16).get(e,t)),objectSize:i.UINT64_LE.get(e,t+16)})};class c{constructor(e){this.len=e.objectSize-t.HeaderObjectToken.len}postProcessTag(e,t,r,n){if("WM/Picture"===t)e.push({id:t,value:b.fromBuffer(n)});else{const i=s.AsfUtil.getParserForAttr(r);if(!i)throw new Error("unexpected value headerType: "+r);e.push({id:t,value:i(n)})}}}t.State=c;t.IgnoreObjectState=class extends c{constructor(e){super(e)}get(e,t){return null}};class u extends c{constructor(e){super(e)}get(e,t){return{fileId:a.default.fromBin(e,t),fileSize:i.UINT64_LE.get(e,t+16),creationDate:i.UINT64_LE.get(e,t+24),dataPacketsCount:i.UINT64_LE.get(e,t+32),playDuration:i.UINT64_LE.get(e,t+40),sendDuration:i.UINT64_LE.get(e,t+48),preroll:i.UINT64_LE.get(e,t+56),flags:{broadcast:n.default.strtokBITSET.get(e,t+64,24),seekable:n.default.strtokBITSET.get(e,t+64,25)},minimumDataPacketSize:i.UINT32_LE.get(e,t+68),maximumDataPacketSize:i.UINT32_LE.get(e,t+72),maximumBitrate:i.UINT32_LE.get(e,t+76)}}}u.guid=a.default.FilePropertiesObject,t.FilePropertiesObject=u;class l extends c{constructor(e){super(e)}get(e,t){return{streamType:a.default.decodeMediaType(a.default.fromBin(e,t)),errorCorrectionType:a.default.fromBin(e,t+8)}}}l.guid=a.default.StreamPropertiesObject,t.StreamPropertiesObject=l;class d{constructor(){this.len=22}get(e,t){return{reserved1:a.default.fromBin(e,t),reserved2:e.readUInt16LE(t+16),extensionDataSize:e.readUInt32LE(t+18)}}}d.guid=a.default.HeaderExtensionObject,t.HeaderExtensionObject=d;class f extends c{constructor(e){super(e)}get(e,t){const r=[];let n=t+10;for(let i=0;i0){const t=f.contentDescTags[i],o=n+a;r.push({id:t,value:s.AsfUtil.parseUnicodeAttr(e.slice(n,o))}),n=o}}return r}}f.guid=a.default.ContentDescriptionObject,f.contentDescTags=["Title","Author","Copyright","Description","Rating"],t.ContentDescriptionObjectState=f;class p extends c{constructor(e){super(e)}get(e,t){const r=[],n=e.readUInt16LE(t);let i=t+2;for(let t=0;t({lastBlock:n.default.strtokBITSET.get(e,t,7),type:n.default.getBitAllignedNumber(e,t,1,7),length:i.UINT24_BE.get(e,t+1)})},d.BlockStreamInfo={len:34,get:(e,t)=>({minimumBlockSize:i.UINT16_BE.get(e,t),maximumBlockSize:i.UINT16_BE.get(e,t+2)/1e3,minimumFrameSize:i.UINT24_BE.get(e,t+4),maximumFrameSize:i.UINT24_BE.get(e,t+7),sampleRate:i.UINT24_BE.get(e,t+10)>>4,channels:n.default.getBitAllignedNumber(e,t+12,4,3)+1,bitsPerSample:n.default.getBitAllignedNumber(e,t+12,7,5)+1,totalSamples:n.default.getBitAllignedNumber(e,t+13,4,36),fileMD5:new i.BufferType(16).get(e,t+18)})};class f{constructor(e){this.data=e,this.offset=0}readInt32(){const e=i.UINT32_LE.get(this.data,this.offset);return this.offset+=4,e}readStringUtf8(){const e=this.readInt32(),t=this.data.toString("utf8",this.offset,this.offset+e);return this.offset+=e,t}}},function(e,t,r){"use strict";(function(e){Object.defineProperty(t,"__esModule",{value:!0});const n=r(2),i=r(1),a=r(7),s=r(245),o=r(44),c=r(25),u=n("music-metadata:parser:MP4"),l="iTunes",d={raw:{lossy:!1,format:"raw"},MAC3:{lossy:!0,format:"MACE 3:1"},MAC6:{lossy:!0,format:"MACE 6:1"},ima4:{lossy:!0,format:"IMA 4:1"},ulaw:{lossy:!0,format:"uLaw 2:1"},alaw:{lossy:!0,format:"uLaw 2:1"},Qclp:{lossy:!0,format:"QUALCOMM PureVoice"},".mp3":{lossy:!0,format:"MPEG-1 layer 3"},alac:{lossy:!1,format:"ALAC"},"ac-3":{lossy:!0,format:"AC-3"},mp4a:{lossy:!0,format:"MPEG-4/AAC"},mp4s:{lossy:!0,format:"MP4S"},c608:{lossy:!0,format:"CEA-608"},c708:{lossy:!0,format:"CEA-708"}};function f(e,t,r){return r.indexOf(e)===t}class p extends a.BasicParser{static read_BE_Signed_Integer(e){return i.readIntBE(e,0,e.length)}static read_BE_Unsigned_Integer(e){return i.readUIntBE(e,0,e.length)}async parse(){this.formatList=[];const e=new s.Atom({name:"mp4",length:this.tokenizer.fileSize},!1,null);await e.readAtoms(this.tokenizer,async e=>{if(e.parent)switch(e.parent.header.name){case"ilst":case"":return this.parseMetadataItemData(e);case"stbl":switch(e.header.name){case"stsd":return this.parseAtom_stsd(e.dataLen);default:u(`Ignore: stbl/${e.header.name} atom`)}}switch(e.header.name){case"ftyp":const t=await this.parseAtom_ftyp(e.dataLen);u(`ftyp: ${t.join("/")}`);const r=t.filter(f).join("/");return void this.metadata.setFormat("container",r);case"mdhd":return this.parseAtom_mdhd(e);case"mvhd":return this.parseAtom_mvhd(e);case"mdat":this.audioLengthInBytes=e.dataLen,this.calculateBitRate()}await this.tokenizer.readToken(new i.IgnoreType(e.dataLen)),u(`Ignore atom data: path=${e.atomPath}, payload-len=${e.dataLen}`)},this.tokenizer.fileSize),this.metadata.setFormat("codec",this.formatList.filter(f).join("+"))}calculateBitRate(){this.audioLengthInBytes&&this.metadata.format.duration&&this.metadata.setFormat("bitrate",8*this.audioLengthInBytes/this.metadata.format.duration)}addTag(e,t){this.metadata.addTag(l,e,t)}addWarning(e){u("Warning:"+e),this.warnings.push(e)}parseMetadataItemData(e){let t=e.header.name;return e.readAtoms(this.tokenizer,async e=>{switch(e.header.name){case"data":return this.parseValueAtom(t,e);case"name":const r=await this.tokenizer.readToken(new o.NameAtom(e.dataLen));t+=":"+r.name;break;case"mean":const n=await this.tokenizer.readToken(new o.NameAtom(e.dataLen));t+=":"+n.name;break;default:const a=await this.tokenizer.readToken(new i.BufferType(e.dataLen));this.addWarning("Unsupported meta-item: "+t+"["+e.header.name+"] => value="+a.toString("hex")+" ascii="+a.toString("ascii"))}},e.dataLen)}async parseValueAtom(t,r){const n=await this.tokenizer.readToken(new o.DataAtom(r.header.length-o.Header.len));if(0!==n.type.set)throw new Error("Unsupported type-set != 0: "+n.type.set);switch(n.type.type){case 0:switch(t){case"trkn":case"disk":const e=i.UINT8.get(n.value,3),r=i.UINT8.get(n.value,5);this.addTag(t,e+"/"+r);break;case"gnre":const a=i.UINT8.get(n.value,1),s=c.Genres[a-1];this.addTag(t,s)}break;case 1:case 18:this.addTag(t,n.value.toString("utf-8"));break;case 13:if(this.options.skipCovers)break;this.addTag(t,{format:"image/jpeg",data:e.from(n.value)});break;case 14:if(this.options.skipCovers)break;this.addTag(t,{format:"image/png",data:e.from(n.value)});break;case 21:this.addTag(t,p.read_BE_Signed_Integer(n.value));break;case 22:this.addTag(t,p.read_BE_Unsigned_Integer(n.value));break;case 65:this.addTag(t,n.value.readInt8(0));break;case 66:this.addTag(t,n.value.readInt16BE(0));break;case 67:this.addTag(t,n.value.readInt32BE(0));break;default:this.addWarning(`atom key=${t}, has unknown well-known-type (data-type): ${n.type.type}`)}}async parseAtom_mvhd(e){const t=await this.tokenizer.readToken(new o.MvhdAtom(e.dataLen));this.parse_mxhd(t)}async parseAtom_mdhd(e){const t=await this.tokenizer.readToken(new o.MdhdAtom(e.dataLen));this.parse_mxhd(t)}parse_mxhd(e){if(e.timeScale&&!this.metadata.format.duration){const t=e.duration/e.timeScale;this.metadata.setFormat("duration",t),this.calculateBitRate()}}async parseAtom_ftyp(e){const t=await this.tokenizer.readToken(o.ftyp);if((e-=o.ftyp.len)>0){const r=await this.parseAtom_ftyp(e),n=t.type.replace(/\W/g,"");return n.length>0&&r.push(n),r}return[]}async parseAtom_stsd(e){const t=await this.tokenizer.readToken(new o.StsdAtom(e)),r=[];for(const e of t.table){const t=d[e.dataFormat];t?(this.parseSoundSampleDescription(e),this.metadata.setFormat("lossless",!t.lossy),r.push(t.format)):u(`Warning: data-format '${e.dataFormat}' missing in MP4Parser.encoderDict`)}r.length>0&&this.formatList.push(r.join("/"))}parseSoundSampleDescription(e){let t=0;const r=o.SoundSampleDescriptionVersion.get(e.description,t);if(t+=o.SoundSampleDescriptionVersion.len,0===r.version||1===r.version){const r=o.SoundSampleDescriptionV0.get(e.description,t);this.metadata.setFormat("sampleRate",r.sampleRate),this.metadata.setFormat("bitsPerSample",r.sampleSize),this.metadata.setFormat("numberOfChannels",r.numAudioChannels)}else u(`Warning: sound-sample-description ${r} not implemented`)}}t.MP4Parser=p}).call(this,r(3).Buffer)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(10),i=r(2),a=r(1),s=r(44),o=i("music-metadata:parser:MP4:Atom");class c{constructor(e,t,r){this.header=e,this.extended=t,this.parent=r,this.children=[],this.atomPath=(this.parent?this.parent.atomPath+"/":"")+this.header.name,this.dataLen=this.header.length-(t?16:8)}async readAtoms(e,t,r){const i=await this.readAtom(e,t);return this.children.push(i),void 0===r?this.readAtoms(e,t,r).catch(e=>{if(e.message!==n.endOfFile)throw e;o("Reached end-of-file")}):(r-=i.header.length)>0?this.readAtoms(e,t,r):void 0}async readAtom(e,t){const r=e.position,n=await e.readToken(s.Header),i=1===n.length;i&&(n.length=await e.readToken(s.ExtendedSize));const a=new c(n,i,this);return o(`parse atom name=${a.atomPath}, extended=${a.extended}, offset=${r}, len=${a.header.length}`),await a.readData(e,t),a}async readData(e,t){switch(this.header.name){case"moov":case"udta":case"trak":case"mdia":case"minf":case"stbl":case"":case"ilst":return this.readAtoms(e,t,this.dataLen);case"meta":return await e.readToken(new a.IgnoreType(4)),this.readAtoms(e,t,this.dataLen-4);case"mdhd":case"mvhd":case"tkhd":case"stsz":case"mdat":default:return t(this)}}}t.Atom=c},function(e,t,r){"use strict";(function(e){Object.defineProperty(t,"__esModule",{value:!0});const n=r(6),i=r(1),a=r(10),s=r(2),o=r(4),c=r(24),u=r(247),l=s("music-metadata:parser:mpeg"),d=1024,f={AudioObjectTypes:["AAC Main","AAC LC","AAC SSR","AAC LTP"],SamplingFrequencies:[96e3,88200,64e3,48e3,44100,32e3,24e3,22050,16e3,12e3,11025,8e3,7350,void 0,void 0,-1]},p=[void 0,["front-center"],["front-left","front-right"],["front-center","front-left","front-right"],["front-center","front-left","front-right","back-center"],["front-center","front-left","front-right","back-left","back-right"],["front-center","front-left","front-right","back-left","back-right","LFE-channel"],["front-center","front-left","front-right","side-left","side-right","back-left","back-right","LFE-channel"]];class h{constructor(e,t){this.versionIndex=o.default.getBitAllignedNumber(e,t+1,3,2),this.layer=h.LayerDescription[o.default.getBitAllignedNumber(e,t+1,5,2)],this.versionIndex>1&&0===this.layer?this.parseAdtsHeader(e,t):this.parseMpegHeader(e,t),this.isProtectedByCRC=!o.default.isBitSet(e,t+1,7)}calcDuration(e){return e*this.calcSamplesPerFrame()/this.samplingRate}calcSamplesPerFrame(){return h.samplesInFrameTable[1===this.version?0:1][this.layer]}calculateSideInfoLength(){if(3!==this.layer)return 2;if(3===this.channelModeIndex){if(1===this.version)return 17;if(2===this.version||2.5===this.version)return 9}else{if(1===this.version)return 32;if(2===this.version||2.5===this.version)return 17}}calcSlotSize(){return[null,4,1,1][this.layer]}parseMpegHeader(e,t){this.container="MPEG",this.bitrateIndex=o.default.getBitAllignedNumber(e,t+2,0,4),this.sampRateFreqIndex=o.default.getBitAllignedNumber(e,t+2,4,2),this.padding=o.default.isBitSet(e,t+2,6),this.privateBit=o.default.isBitSet(e,t+2,7),this.channelModeIndex=o.default.getBitAllignedNumber(e,t+3,0,2),this.modeExtension=o.default.getBitAllignedNumber(e,t+3,2,2),this.isCopyrighted=o.default.isBitSet(e,t+3,4),this.isOriginalMedia=o.default.isBitSet(e,t+3,5),this.emphasis=o.default.getBitAllignedNumber(e,t+3,7,2),this.version=h.VersionID[this.versionIndex],this.channelMode=h.ChannelMode[this.channelModeIndex],this.codec="MP"+this.layer;const r=this.calcBitrate();if(!r)throw new Error("Cannot determine bit-rate");if(this.bitrate=1e3*r,this.samplingRate=this.calcSamplingRate(),null==this.samplingRate)throw new Error("Cannot determine sampling-rate")}parseAdtsHeader(e,t){l("layer=0 => ADTS"),this.version=2===this.versionIndex?4:2,this.container="ADTS/MPEG-"+this.version;const r=o.default.getBitAllignedNumber(e,t+2,0,2);this.codec="AAC",this.codecProfile=f.AudioObjectTypes[r],l(`MPEG-4 audio-codec=${this.codec}`);const n=o.default.getBitAllignedNumber(e,t+2,2,4);this.samplingRate=f.SamplingFrequencies[n],l(`sampling-rate=${this.samplingRate}`);const i=o.default.getBitAllignedNumber(e,t+2,7,3);this.mp4ChannelConfig=p[i],l(`channel-config=${this.mp4ChannelConfig.join("+")}`),this.frameLength=o.default.getBitAllignedNumber(e,t+3,6,2)<<11}calcBitrate(){if(0===this.bitrateIndex)return null;if(15===this.bitrateIndex)return null;const e=this.version.toString()+this.layer;return h.bitrate_index[this.bitrateIndex][e]}calcSamplingRate(){return 3===this.sampRateFreqIndex?null:h.sampling_rate_freq_index[this.version][this.sampRateFreqIndex]}}h.SyncByte1=255,h.SyncByte2=224,h.VersionID=[2.5,null,2,1],h.LayerDescription=[0,3,2,1],h.ChannelMode=["stereo","joint_stereo","dual_channel","mono"],h.bitrate_index={1:{11:32,12:32,13:32,21:32,22:8,23:8},2:{11:64,12:48,13:40,21:48,22:16,23:16},3:{11:96,12:56,13:48,21:56,22:24,23:24},4:{11:128,12:64,13:56,21:64,22:32,23:32},5:{11:160,12:80,13:64,21:80,22:40,23:40},6:{11:192,12:96,13:80,21:96,22:48,23:48},7:{11:224,12:112,13:96,21:112,22:56,23:56},8:{11:256,12:128,13:112,21:128,22:64,23:64},9:{11:288,12:160,13:128,21:144,22:80,23:80},10:{11:320,12:192,13:160,21:160,22:96,23:96},11:{11:352,12:224,13:192,21:176,22:112,23:112},12:{11:384,12:256,13:224,21:192,22:128,23:128},13:{11:416,12:320,13:256,21:224,22:144,23:144},14:{11:448,12:384,13:320,21:256,22:160,23:160}},h.sampling_rate_freq_index={1:{0:44100,1:48e3,2:32e3},2:{0:22050,1:24e3,2:16e3},2.5:{0:11025,1:12e3,2:8e3}},h.samplesInFrameTable=[[0,384,1152,1152],[0,384,1152,576]];const m={len:4,get:(e,t)=>new h(e,t)};t.MpegParser=class extends c.AbstractID3Parser{constructor(){super(...arguments),this.frameCount=0,this.syncFrameCount=-1,this.countSkipFrameData=0,this.totalDataLength=0,this.bitrates=[],this.calculateEofDuration=!1,this.buf_frame_header=e.alloc(4),this.syncPeek={buf:e.alloc(d),len:0}}async _parse(){this.metadata.setFormat("lossless",!1);try{let e=!1;for(;!e;)await this.sync(),e=await this.parseCommonMpegHeader()}catch(e){if(e.message!==a.endOfFile)throw e;if(this.calculateEofDuration){const e=this.frameCount*this.samplesPerFrame;this.metadata.setFormat("numberOfSamples",e);const t=e/this.metadata.format.sampleRate;l(`Calculate duration at EOF: ${t} sec.`,t),this.metadata.setFormat("duration",t)}}}finalize(){const e=this.metadata.format,t=this.metadata.native.hasOwnProperty("ID3v1");if(e.duration&&this.tokenizer.fileSize){const r=this.tokenizer.fileSize-this.mpegOffset-(t?128:0);e.codecProfile&&"V"===e.codecProfile[0]&&this.metadata.setFormat("bitrate",8*r/e.duration)}else if(this.tokenizer.fileSize&&"CBR"===e.codecProfile){const r=this.tokenizer.fileSize-this.mpegOffset-(t?128:0),n=Math.round(r/this.frame_size)*this.samplesPerFrame;this.metadata.setFormat("numberOfSamples",n);const i=n/e.sampleRate;l("Calculate CBR duration based on file size: %s",i),this.metadata.setFormat("duration",i)}}async sync(){let e=!1;for(;;){let t=0;if(this.syncPeek.len=await this.tokenizer.peekBuffer(this.syncPeek.buf,0,d,this.tokenizer.position,!0),this.syncPeek.len<=256)throw new Error(a.endOfFile);if(0===this.syncPeek.len)throw new Error(a.endOfFile);for(;;){if(e&&224==(224&this.syncPeek.buf[t]))return this.buf_frame_header[0]=h.SyncByte1,this.buf_frame_header[1]=this.syncPeek.buf[t],await this.tokenizer.ignore(t),l(`Sync at offset=${this.tokenizer.position-1}, frameCount=${this.frameCount}`),this.syncFrameCount===this.frameCount&&(l(`Re-synced MPEG stream, frameCount=${this.frameCount}`),this.frameCount=0,this.frame_size=0),void(this.syncFrameCount=this.frameCount);if(e=!1,-1===(t=this.syncPeek.buf.indexOf(h.SyncByte1,t))){if(this.syncPeek.len=2&&0===e.layer?this.parseAdts(e):this.parseAudioFrameHeader(e)}async parseAudioFrameHeader(e){this.metadata.setFormat("numberOfChannels","mono"===e.channelMode?1:2),this.metadata.setFormat("bitrate",e.bitrate),this.frameCount<2e5&&l("offset=%s MP%s bitrate=%s sample-rate=%s",this.tokenizer.position-4,e.layer,e.bitrate,e.samplingRate);const t=e.calcSlotSize();if(null===t)throw new Error("invalid slot_size");const r=e.calcSamplesPerFrame();l(`samples_per_frame=${r}`);const n=r/8*e.bitrate/e.samplingRate+(e.padding?t:0);if(this.frame_size=Math.floor(n),this.audioFrameHeader=e,this.bitrates.push(e.bitrate),1===this.frameCount)return this.offset=m.len,await this.skipSideInformation(),!1;if(3===this.frameCount){if(this.areAllSame(this.bitrates)){if(this.samplesPerFrame=r,this.metadata.setFormat("codecProfile","CBR"),this.tokenizer.fileSize)return!0}else if(this.metadata.format.duration)return!0;if(!this.options.duration)return!0}return this.options.duration&&4===this.frameCount&&(this.samplesPerFrame=r,this.calculateEofDuration=!0),this.offset=4,e.isProtectedByCRC?(await this.parseCrc(),!1):(await this.skipSideInformation(),!1)}async parseAdts(t){const r=e.alloc(3);await this.tokenizer.readBuffer(r),t.frameLength+=o.default.getBitAllignedNumber(r,0,0,11),this.tokenizer.ignore(t.frameLength-7),this.totalDataLength+=t.frameLength,this.samplesPerFrame=1024;const n=t.samplingRate/this.samplesPerFrame,i=8*(0===this.frameCount?0:this.totalDataLength/this.frameCount)*n+.5;if(this.metadata.setFormat("codecProfile",t.codecProfile),this.metadata.setFormat("bitrate",i),t.mp4ChannelConfig&&this.metadata.setFormat("numberOfChannels",t.mp4ChannelConfig.length),l(`frame-count=${this.frameCount}, size=${t.frameLength} bytes, bit-rate=${i}`),3===this.frameCount){if(!this.options.duration)return!0;this.calculateEofDuration=!0}return!1}async parseCrc(){return this.crc=await this.tokenizer.readNumber(i.INT16_BE),this.offset+=2,this.skipSideInformation()}async skipSideInformation(){const e=this.audioFrameHeader.calculateSideInfoLength();await this.tokenizer.readToken(new i.BufferType(e)),this.offset+=e,await this.readXtraInfoHeader()}async readXtraInfoHeader(){const e=await this.tokenizer.readToken(u.InfoTagHeaderTag);switch(this.offset+=u.InfoTagHeaderTag.len,e){case"Info":return this.metadata.setFormat("codecProfile","CBR"),this.readXingInfoHeader();case"Xing":const t=await this.readXingInfoHeader(),r="V"+(100-t.vbrScale)/10;return this.metadata.setFormat("codecProfile",r),null;case"Xtra":break;case"LAME":const n=await this.tokenizer.readToken(u.LameEncoderVersion);return this.offset+=u.LameEncoderVersion.len,this.metadata.setFormat("tool","LAME "+n),await this.skipFrameData(this.frame_size-this.offset),null}const t=this.frame_size-this.offset;return t<0?this.warnings.push("Frame "+this.frameCount+"corrupt: negative frameDataLeft"):await this.skipFrameData(t),null}async readXingInfoHeader(){const e=await this.tokenizer.readToken(u.XingInfoTag);if(this.offset+=u.XingInfoTag.len,this.metadata.setFormat("tool",o.default.stripNulls(e.codec)),1==(1&e.headerFlags[3])){const t=this.audioFrameHeader.calcDuration(e.numFrames);return this.metadata.setFormat("duration",t),l("Get duration from Xing header: %s",this.metadata.format.duration),e}const t=this.frame_size-this.offset;return await this.skipFrameData(t),e}async skipFrameData(e){n.ok(e>=0,"frame-data-left cannot be negative"),await this.tokenizer.readToken(new i.IgnoreType(e)),this.countSkipFrameData+=e}areAllSame(e){const t=e[0];return e.every(e=>e===t)}}}).call(this,r(3).Buffer)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1);t.InfoTagHeaderTag=new n.StringType(4,"ascii"),t.LameEncoderVersion=new n.StringType(6,"ascii"),t.XingInfoTag={len:136,get:(e,t)=>({headerFlags:new n.BufferType(4).get(e,t),numFrames:n.UINT32_BE.get(e,t+4),streamSize:n.UINT32_BE.get(e,t+8),vbrScale:n.UINT32_BE.get(e,t+112),codec:new n.StringType(9,"ascii").get(e,t+116),infoTagRevision:n.UINT8.get(e,t+125)>>4,vbrMethod:15&n.UINT8.get(e,t+125)})}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(2),i=r(1),a=r(249),s=r(251),o=r(24),c=n("music-metadata:parser:musepack");t.default=class extends o.AbstractID3Parser{async _parse(){let e;switch(await this.tokenizer.peekToken(new i.StringType(3,"binary"))){case"MP+":c("Musepack stream-version 7"),e=new s.MpcSv7Parser;break;case"MPC":c("Musepack stream-version 8"),e=new a.MpcSv8Parser;break;default:throw new Error("Invalid Musepack signature prefix")}return e.init(this.metadata,this.tokenizer,this.options),e.parse()}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(2),i=r(6),a=r(7),s=r(250),o=r(23),c=r(5),u=n("music-metadata:parser:musepack");t.MpcSv8Parser=class extends a.BasicParser{constructor(){super(...arguments),this.audioLength=0}async parse(){const e=await this.tokenizer.readToken(c.FourCcToken);return i.equal(e,"MPCK","Magic number"),this.metadata.setFormat("container","Musepack, SV8"),this.parsePacket()}async parsePacket(){const e=new s.StreamReader(this.tokenizer);for(;;){const t=await e.readPacketHeader();switch(u(`packet-header key=${t.key}, payloadLength=${t.payloadLength}`),t.key){case"SH":const r=await e.readStreamHeader(t.payloadLength);this.metadata.setFormat("numberOfSamples",r.sampleCount),this.metadata.setFormat("sampleRate",r.sampleFrequency),this.metadata.setFormat("duration",r.sampleCount/r.sampleFrequency),this.metadata.setFormat("numberOfChannels",r.channelCount);break;case"AP":this.audioLength+=t.payloadLength,await this.tokenizer.ignore(t.payloadLength);break;case"RG":case"EI":case"SO":case"ST":case"CT":await this.tokenizer.ignore(t.payloadLength);break;case"SE":return this.metadata.setFormat("bitrate",8*this.audioLength/this.metadata.format.duration),o.APEv2Parser.parseTagHeader(this.metadata,this.tokenizer,this.options);default:throw new Error(`Unexpected header: ${t.key}`)}}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(4),a=r(2)("music-metadata:parser:musepack:sv8"),s=new n.StringType(2,"binary"),o={len:5,get:(e,t)=>({crc:n.UINT32_LE.get(e,t),streamVersion:n.UINT8.get(e,t+4)})},c={len:2,get:(e,t)=>({sampleFrequency:[44100,48e3,37800,32e3][i.default.getBitAllignedNumber(e,t,0,3)],maxUsedBands:i.default.getBitAllignedNumber(e,t,3,5),channelCount:i.default.getBitAllignedNumber(e,t+1,0,4)+1,msUsed:i.default.isBitSet(e,t+1,4),audioBlockFrames:i.default.getBitAllignedNumber(e,t+1,5,3)})};t.StreamReader=class{constructor(e){this.tokenizer=e}async readPacketHeader(){const e=await this.tokenizer.readToken(s),t=await this.readVariableSizeField();return{key:e,payloadLength:t.value-2-t.len}}async readStreamHeader(e){const t={};a(`Reading SH at offset=${this.tokenizer.position}`);const r=await this.tokenizer.readToken(o);e-=o.len,Object.assign(t,r),a(`SH.streamVersion = ${r.streamVersion}`);const n=await this.readVariableSizeField();e-=n.len,t.sampleCount=n.value;const i=await this.readVariableSizeField();e-=i.len,t.beginningOfSilence=i.value;const s=await this.tokenizer.readToken(c);return e-=c.len,Object.assign(t,s),await this.tokenizer.ignore(e),t}async readVariableSizeField(e=1,t=0){let r=await this.tokenizer.readToken(n.UINT8);return 0==(128&r)?{len:e,value:t+r}:(r&=127,r+=t,this.readVariableSizeField(e+1,r<<7))}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(2),i=r(6),a=r(7),s=r(252),o=r(23),c=r(253),u=n("music-metadata:parser:musepack");t.MpcSv7Parser=class extends a.BasicParser{constructor(){super(...arguments),this.audioLength=0}async parse(){const e=await this.tokenizer.readToken(s.Header);i.equal(e.signature,"MP+","Magic number"),u(`stream-version=${e.streamMajorVersion}.${e.streamMinorVersion}`),this.metadata.setFormat("container","Musepack, SV7"),this.metadata.setFormat("sampleRate",e.sampleFrequency);const t=1152*(e.frameCount-1)+e.lastFrameLength;this.metadata.setFormat("numberOfSamples",t),this.duration=t/e.sampleFrequency,this.metadata.setFormat("duration",this.duration),this.bitreader=new c.BitReader(this.tokenizer),this.metadata.setFormat("numberOfChannels",e.midSideStereo||e.intensityStereo?2:1);const r=await this.bitreader.read(8);return this.metadata.setFormat("codec",(r/100).toFixed(2)),await this.skipAudioData(e.frameCount),u(`End of audio stream, switching to APEv2, offset=${this.tokenizer.position}`),o.APEv2Parser.parseTagHeader(this.metadata,this.tokenizer,this.options)}async skipAudioData(e){for(;e-- >0;){const e=await this.bitreader.read(20);this.audioLength+=20+e,await this.bitreader.ignore(e)}const t=await this.bitreader.read(11);this.audioLength+=t,this.metadata.setFormat("bitrate",this.audioLength/this.duration)}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(4);t.Header={len:24,get:(e,t)=>{const r={signature:e.toString("binary",t,t+3),streamMinorVersion:i.default.getBitAllignedNumber(e,t+3,0,4),streamMajorVersion:i.default.getBitAllignedNumber(e,t+3,4,4),frameCount:n.UINT32_LE.get(e,t+4),maxLevel:n.UINT16_LE.get(e,t+8),sampleFrequency:[44100,48e3,37800,32e3][i.default.getBitAllignedNumber(e,t+10,0,2)],link:i.default.getBitAllignedNumber(e,t+10,2,2),profile:i.default.getBitAllignedNumber(e,t+10,4,4),maxBand:i.default.getBitAllignedNumber(e,t+11,0,6),intensityStereo:i.default.isBitSet(e,t+11,6),midSideStereo:i.default.isBitSet(e,t+11,7),titlePeak:n.UINT16_LE.get(e,t+12),titleGain:n.UINT16_LE.get(e,t+14),albumPeak:n.UINT16_LE.get(e,t+16),albumGain:n.UINT16_LE.get(e,t+18),lastFrameLength:n.UINT32_LE.get(e,t+20)>>>20&2047,trueGapless:i.default.isBitSet(e,t+23,0)};return r.lastFrameLength=r.trueGapless?n.UINT32_LE.get(e,20)>>>20&2047:0,r}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1);t.BitReader=class{constructor(e){this.tokenizer=e,this.pos=0,this.dword=void 0}async read(e){for(;void 0===this.dword;)this.dword=await this.tokenizer.readToken(n.UINT32_LE);let t=this.dword;return this.pos+=e,this.pos<32?(t>>>=32-this.pos)&(1<>>32-this.pos),t&(1<0){const t=32-this.pos;this.dword=void 0,e-=t,this.pos=0}const t=e%32,r=(e-t)/32;return await this.tokenizer.ignore(4*r),this.read(t)}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(2),a=r(6),s=r(4),o=r(5),c=r(31),u=r(255),l=r(257),d=r(7),f=r(259),p=i("music-metadata:parser:ogg");class h{static sum(e,t,r){let n=0;for(let i=t;i0)return this.warnings.push("Invalid FourCC ID, maybe last OGG-page is not marked with last-page flag"),this.pageConsumer.flush();throw e}}}m.Header={len:27,get:(e,t)=>({capturePattern:o.FourCcToken.get(e,t),version:e.readUInt8(t+4),headerType:{continued:s.default.strtokBITSET.get(e,t+5,0),firstPage:s.default.strtokBITSET.get(e,t+5,1),lastPage:s.default.strtokBITSET.get(e,t+5,2)},absoluteGranulePosition:e.readIntLE(t+6,6),streamSerialNumber:n.UINT32_LE.get(e,t+14),pageSequenceNo:n.UINT32_LE.get(e,t+18),pageChecksum:n.UINT32_LE.get(e,t+22),page_segments:e.readUInt8(t+26)})},t.OggParser=m},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(256),a=r(31);t.OpusParser=class extends a.VorbisParser{constructor(e,t,r){super(e,t),this.tokenizer=r,this.lastPos=-1}parseFirstPage(e,t){if(this.metadata.setFormat("codec","Opus"),this.idHeader=new i.IdHeader(t.length).get(t,0),"OpusHead"!==this.idHeader.magicSignature)throw new Error("Illegal ogg/Opus magic-signature");this.metadata.setFormat("sampleRate",this.idHeader.inputSampleRate),this.metadata.setFormat("numberOfChannels",this.idHeader.channelCount)}parseFullPage(e){switch(new n.StringType(8,"ascii").get(e,0)){case"OpusTags":this.parseUserCommentList(e,8),this.lastPos=this.tokenizer.position}}calculateDuration(e){if(this.metadata.format.sampleRate&&e.absoluteGranulePosition>=0&&(this.metadata.setFormat("numberOfSamples",e.absoluteGranulePosition-this.idHeader.preSkip),this.metadata.setFormat("duration",this.metadata.format.numberOfSamples/this.idHeader.inputSampleRate),-1!==this.lastPos&&this.tokenizer.fileSize&&this.metadata.format.duration)){const e=this.tokenizer.fileSize-this.lastPos;this.metadata.setFormat("bitrate",8*e/this.metadata.format.duration)}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1);t.IdHeader=class{constructor(e){if(this.len=e,e<19)throw new Error("ID-header-page 0 should be at least 19 bytes long")}get(e,t){return{magicSignature:new n.StringType(8,"ascii").get(e,t+0),version:e.readUInt8(t+8),channelCount:e.readUInt8(t+9),preSkip:e.readInt16LE(t+10),inputSampleRate:e.readInt32LE(t+12),outputGain:e.readInt16LE(t+16),channelMapping:e.readUInt8(t+18)}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(2),i=r(258),a=r(31),s=n("music-metadata:parser:ogg:speex");t.SpeexParser=class extends a.VorbisParser{constructor(e,t,r){super(e,t),this.tokenizer=r}parseFirstPage(e,t){s("First Ogg/Speex page");const r=i.Header.get(t,0);this.metadata.setFormat("codec",`Speex ${r.version}`),this.metadata.setFormat("numberOfChannels",r.nb_channels),this.metadata.setFormat("sampleRate",r.rate),-1!==r.bitrate&&this.metadata.setFormat("bitrate",r.bitrate)}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(4);t.Header={len:80,get:(e,t)=>({speex:new n.StringType(8,"ascii").get(e,t+0),version:i.default.trimRightNull(new n.StringType(20,"ascii").get(e,t+8)),version_id:e.readInt32LE(t+28),header_size:e.readInt32LE(t+32),rate:e.readInt32LE(t+36),mode:e.readInt32LE(t+40),mode_bitstream_version:e.readInt32LE(t+44),nb_channels:e.readInt32LE(t+48),bitrate:e.readInt32LE(t+52),frame_size:e.readInt32LE(t+56),vbr:e.readInt32LE(t+60),frames_per_packet:e.readInt32LE(t+64),extra_headers:e.readInt32LE(t+68),reserved1:e.readInt32LE(t+72),reserved2:e.readInt32LE(t+76)})}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(2),i=r(260),a=n("music-metadata:parser:ogg:theora");t.TheoraParser=class{constructor(e,t,r){this.metadata=e,this.tokenizer=r}parsePage(e,t){e.headerType.firstPage&&this.parseFirstPage(e,t)}flush(){a("flush")}parseFirstPage(e,t){a("First Ogg/Theora page"),this.metadata.setFormat("codec","Theora");const r=i.IdentificationHeader.get(t,0);this.metadata.setFormat("bitrate",r.nombr)}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1);t.IdentificationHeader={len:42,get:(e,t)=>({id:new n.StringType(7,"ascii").get(e,t),vmaj:e.readUInt8(t+7),vmin:e.readUInt8(t+8),vrev:e.readUInt8(t+9),vmbw:e.readUInt16BE(t+10),vmbh:e.readUInt16BE(t+17),nombr:n.UINT24_BE.get(e,t+37),nqual:e.readUInt8(t+40)})}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(10),i=r(21),a=r(1),s=r(2),o=r(262),c=r(263),u=r(17),l=r(4),d=r(5),f=r(7),p=r(26),h=s("music-metadata:parser:RIFF");t.WaveParser=class extends f.BasicParser{async parse(){const e=await this.tokenizer.readToken(o.Header);if(h(`pos=${this.tokenizer.position}, parse: chunkID=${e.chunkID}`),"RIFF"===e.chunkID)return this.parseRiffChunk().catch(e=>{if(e.message!==n.endOfFile)throw e})}async parseRiffChunk(){const e=await this.tokenizer.readToken(d.FourCcToken);switch(this.metadata.setFormat("container",e),e){case"WAVE":return this.readWaveChunk();default:throw new Error(`Unsupported RIFF format: RIFF/${e}`)}}async readWaveChunk(){for(;;){const e=await this.tokenizer.readToken(o.Header);switch(this.header=e,h(`pos=${this.tokenizer.position}, readChunk: chunkID=RIFF/WAVE/${e.chunkID}`),e.chunkID){case"LIST":await this.parseListTag(e);break;case"fact":this.metadata.setFormat("lossless",!1),this.fact=await this.tokenizer.readToken(new c.FactChunk(e));break;case"fmt ":const t=await this.tokenizer.readToken(new c.Format(e));let r=c.WaveFormat[t.wFormatTag];r||(h("WAVE/non-PCM format="+t.wFormatTag),r="non-PCM ("+t.wFormatTag+")"),this.metadata.setFormat("codec",r),this.metadata.setFormat("bitsPerSample",t.wBitsPerSample),this.metadata.setFormat("sampleRate",t.nSamplesPerSec),this.metadata.setFormat("numberOfChannels",t.nChannels),this.metadata.setFormat("bitrate",t.nBlockAlign*t.nSamplesPerSec*8),this.blockAlign=t.nBlockAlign;break;case"id3 ":case"ID3 ":const n=await this.tokenizer.readToken(new a.BufferType(e.chunkSize)),s=new p.ID3Stream(n),o=i.fromStream(s);await(new u.ID3v2Parser).parse(this.metadata,o,this.options);break;case"data":!1!==this.metadata.format.lossless&&this.metadata.setFormat("lossless",!0);const l=this.fact?this.fact.dwSampleLength:e.chunkSize/this.blockAlign;this.metadata.setFormat("numberOfSamples",l),this.metadata.setFormat("duration",l/this.metadata.format.sampleRate),this.metadata.setFormat("bitrate",this.metadata.format.numberOfChannels*this.blockAlign*this.metadata.format.sampleRate),await this.tokenizer.ignore(e.chunkSize);break;default:h(`Ignore chunk: RIFF/${e.chunkID} of ${e.chunkSize} bytes`),this.warnings.push("Ignore chunk: RIFF/"+e.chunkID),await this.tokenizer.ignore(e.chunkSize)}this.header.chunkSize%2==1&&(h("Read odd padding byte"),await this.tokenizer.ignore(1))}}async parseListTag(e){const t=await this.tokenizer.readToken(d.FourCcToken);switch(h("pos=%s, parseListTag: chunkID=RIFF/WAVE/LIST/%s",this.tokenizer.position,t),t){case"INFO":return this.parseRiffInfoTags(e.chunkSize-4);case"adtl":default:return this.warnings.push("Ignore chunk: RIFF/WAVE/LIST/"+t),h("Ignoring chunkID=RIFF/WAVE/LIST/"+t),this.tokenizer.ignore(e.chunkSize-4)}}async parseRiffInfoTags(e){for(;e>=8;){const t=await this.tokenizer.readToken(o.Header),r=new o.ListInfoTagValue(t),n=await this.tokenizer.readToken(r);this.addTag(t.chunkID,l.default.stripNulls(n)),e-=8+r.len}if(0!==e)throw Error("Illegal remaining size: "+e)}addTag(e,t){this.metadata.addTag("exif",e,t)}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(5);t.Header={len:8,get:(e,t)=>({chunkID:i.FourCcToken.get(e,t),chunkSize:e.readUInt32LE(t+4)})};t.ListInfoTagValue=class{constructor(e){this.tagHeader=e,this.len=e.chunkSize,this.len+=1&this.len}get(e,t){return new n.StringType(this.tagHeader.chunkSize,"ascii").get(e,t)}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(6);!function(e){e[e.PCM=1]="PCM",e[e.ADPCM=2]="ADPCM",e[e.IEEE_FLOAT=3]="IEEE_FLOAT",e[e.MPEG_ADTS_AAC=5632]="MPEG_ADTS_AAC",e[e.MPEG_LOAS=5634]="MPEG_LOAS",e[e.RAW_AAC1=255]="RAW_AAC1",e[e.DOLBY_AC3_SPDIF=146]="DOLBY_AC3_SPDIF",e[e.DVM=8192]="DVM",e[e.RAW_SPORT=576]="RAW_SPORT",e[e.ESST_AC3=577]="ESST_AC3",e[e.DRM=9]="DRM",e[e.DTS2=8193]="DTS2",e[e.MPEG=80]="MPEG"}(t.WaveFormat||(t.WaveFormat={}));t.Format=class{constructor(e){n.ok(e.chunkSize>=16,"16 for PCM."),this.len=e.chunkSize}get(e,t){return{wFormatTag:e.readUInt16LE(t),nChannels:e.readUInt16LE(t+2),nSamplesPerSec:e.readUInt32LE(t+4),nAvgBytesPerSec:e.readUInt32LE(t+8),nBlockAlign:e.readUInt16LE(t+12),wBitsPerSample:e.readUInt16LE(t+14)}}};t.FactChunk=class{constructor(e){n.ok(e.chunkSize>=4,"minimum fact chunk size."),this.len=e.chunkSize}get(e,t){return{dwSampleLength:e.readUInt32LE(t)}}}},function(e,t,r){"use strict";(function(e){Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(6),a=r(23),s=r(5),o=r(7),c=r(265),u=r(2)("music-metadata:parser:WavPack");t.WavPackParser=class extends o.BasicParser{async parse(){return this.audioDataSize=0,await this.parseWavPackBlocks(),a.APEv2Parser.parseTagHeader(this.metadata,this.tokenizer,this.options)}async parseWavPackBlocks(){do{if("wvpk"!==await this.tokenizer.peekToken(s.FourCcToken))break;const e=await this.tokenizer.readToken(c.WavPack.BlockHeaderToken);i.strictEqual(e.BlockID,"wvpk","WavPack Block-ID"),u(`WavPack header blockIndex=${e.blockIndex}, len=${c.WavPack.BlockHeaderToken.len}`),0!==e.blockIndex||this.metadata.format.container||(this.metadata.setFormat("container","WavPack"),this.metadata.setFormat("lossless",!e.flags.isHybrid),this.metadata.setFormat("bitsPerSample",e.flags.bitsPerSample),e.flags.isDSD||(this.metadata.setFormat("sampleRate",e.flags.samplingRate),this.metadata.setFormat("duration",e.totalSamples/e.flags.samplingRate)),this.metadata.setFormat("numberOfChannels",e.flags.isMono?1:2),this.metadata.setFormat("numberOfSamples",e.totalSamples),this.metadata.setFormat("codec",e.flags.isDSD?"DSD":"PCM"));const t=e.blockSize-(c.WavPack.BlockHeaderToken.len-8);0===e.blockIndex?await this.parseMetadataSubBlock(e,t):await this.tokenizer.ignore(t),e.blockSamples>0&&(this.audioDataSize+=e.blockSize)}while(!this.tokenizer.fileSize||this.tokenizer.fileSize-this.tokenizer.position>=c.WavPack.BlockHeaderToken.len);this.metadata.setFormat("bitrate",8*this.audioDataSize/this.metadata.format.duration)}async parseMetadataSubBlock(t,r){for(;r>c.WavPack.MetadataIdToken.len;){const a=await this.tokenizer.readToken(c.WavPack.MetadataIdToken),s=await this.tokenizer.readNumber(a.largeBlock?n.UINT24_LE:n.UINT8),o=e.alloc(2*s-(a.isOddSize?1:0));switch(await this.tokenizer.readBuffer(o,0,o.length),u(`Metadata Sub-Blocks functionId=0x${a.functionId.toString(16)}, id.largeBlock=${a.largeBlock},data-size=${o.length}`),a.functionId){case 0:break;case 14:u("ID_DSD_BLOCK");const e=1<>>t&4294967295>>>32-r}}s.BlockHeaderToken={len:32,get:(e,t)=>{const r=n.UINT32_LE.get(e,t+24),o={BlockID:i.FourCcToken.get(e,t),blockSize:n.UINT32_LE.get(e,t+4),version:n.UINT16_LE.get(e,t+8),totalSamples:(n.UINT8.get(e,t+11)<<32)+n.UINT32_LE.get(e,t+12),blockIndex:(n.UINT8.get(e,t+10)<<32)+n.UINT32_LE.get(e,t+16),blockSamples:n.UINT32_LE.get(e,t+20),flags:{bitsPerSample:8*(1+s.getBitAllignedNumber(r,0,2)),isMono:s.isBitSet(r,2),isHybrid:s.isBitSet(r,3),isJointStereo:s.isBitSet(r,4),crossChannel:s.isBitSet(r,5),hybridNoiseShaping:s.isBitSet(r,6),floatingPoint:s.isBitSet(r,7),samplingRate:a[s.getBitAllignedNumber(r,23,4)],isDSD:s.isBitSet(r,31)},crc:new n.BufferType(4).get(e,t+28)};return o.flags.isDSD&&(o.totalSamples*=8),o}},s.MetadataIdToken={len:1,get:(e,t)=>({functionId:s.getBitAllignedNumber(e[t],0,6),isOptional:s.isBitSet(e[t],5),isOddSize:s.isBitSet(e[t],6),largeBlock:s.isBitSet(e[t],7)})},t.WavPack=s},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(24),i=r(6),a=r(2),s=r(267),o=r(17),c=a("music-metadata:parser:DSF");t.DsfParser=class extends n.AbstractID3Parser{async _parse(){const e=this.tokenizer.position,t=await this.tokenizer.readToken(s.ChunkHeader);i.strictEqual(t.id,"DSD ","Invalid chunk signature"),this.metadata.setFormat("container","DSF"),this.metadata.setFormat("lossless",!0);const r=await this.tokenizer.readToken(s.DsdChunk);if(0!==r.metadataPointer)return c(`expect ID3v2 at offset=${r.metadataPointer}`),await this.parseChunks(r.fileSize-t.size),await this.tokenizer.ignore(r.metadataPointer-this.tokenizer.position-e),(new o.ID3v2Parser).parse(this.metadata,this.tokenizer,this.options);c("No ID3v2 tag present")}async parseChunks(e){for(;e>=s.ChunkHeader.len;){const t=await this.tokenizer.readToken(s.ChunkHeader);switch(c(`Parsing chunk name=${t.id} size=${t.size}`),t.id){case"fmt ":const e=await this.tokenizer.readToken(s.FormatChunk);this.metadata.setFormat("numberOfChannels",e.channelNum),this.metadata.setFormat("sampleRate",e.samplingFrequency),this.metadata.setFormat("bitsPerSample",e.bitsPerSample),this.metadata.setFormat("numberOfSamples",e.sampleCount),this.metadata.setFormat("duration",e.sampleCount/e.samplingFrequency);const r=e.bitsPerSample*e.samplingFrequency*e.channelNum;return void this.metadata.setFormat("bitrate",r);default:this.tokenizer.ignore(t.size-s.ChunkHeader.len)}e-=t.size}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(5);t.ChunkHeader={len:12,get:(e,t)=>({id:i.FourCcToken.get(e,t),size:n.UINT64_LE.get(e,t+4)})},t.DsdChunk={len:16,get:(e,t)=>({fileSize:n.INT64_LE.get(e,t),metadataPointer:n.INT64_LE.get(e,t+8)})},function(e){e[e.mono=1]="mono",e[e.stereo=2]="stereo",e[e.channels=3]="channels",e[e.quad=4]="quad",e[e["4 channels"]=5]="4 channels",e[e["5 channels"]=6]="5 channels",e[e["5.1 channels"]=7]="5.1 channels"}(t.ChannelType||(t.ChannelType={})),t.FormatChunk={len:40,get:(e,t)=>({formatVersion:n.INT32_LE.get(e,t),formatID:n.INT32_LE.get(e,t+4),channelType:n.INT32_LE.get(e,t+8),channelNum:n.INT32_LE.get(e,t+12),samplingFrequency:n.INT32_LE.get(e,t+16),bitsPerSample:n.INT32_LE.get(e,t+20),sampleCount:n.INT64_LE.get(e,t+24),blockSizePerChannel:n.INT32_LE.get(e,t+32)})}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(6),i=r(1),a=r(2),s=r(5),o=r(7),c=r(26),u=r(269),l=r(21),d=r(17),f=a("music-metadata:parser:aiff");t.DsdiffParser=class extends o.BasicParser{async parse(){const e=await this.tokenizer.readToken(u.ChunkHeader);n.strictEqual(e.chunkID,"FRM8");const t=(await this.tokenizer.readToken(s.FourCcToken)).trim();switch(t){case"DSD":return this.metadata.setFormat("container",`DSDIFF/${t}`),this.metadata.setFormat("lossless",!0),this.readFmt8Chunks(e.chunkSize-s.FourCcToken.len);default:throw Error(`Unsupported DSDIFF type: ${t}`)}}async readFmt8Chunks(e){for(;e>=u.ChunkHeader.len;){const t=await this.tokenizer.readToken(u.ChunkHeader);f(`Chunk id=${t.chunkID}`),await this.readData(t),e-=u.ChunkHeader.len+t.chunkSize}}async readData(e){f(`Reading data of chunk[ID=${e.chunkID}, size=${e.chunkSize}]`);const t=this.tokenizer.position;switch(e.chunkID.trim()){case"FVER":const t=await this.tokenizer.readToken(i.UINT32_LE);f(`DSDIFF version=${t}`);break;case"PROP":const r=await this.tokenizer.readToken(s.FourCcToken);n.strictEqual(r,"SND "),await this.handleSoundPropertyChunks(e.chunkSize-s.FourCcToken.len);break;case"ID3":const a=await this.tokenizer.readToken(new i.BufferType(e.chunkSize)),o=new c.ID3Stream(a),u=l.fromStream(o);await(new d.ID3v2Parser).parse(this.metadata,u,this.options);break;default:f(`Ignore chunk[ID=${e.chunkID}, size=${e.chunkSize}]`);break;case"DSD":this.metadata.setFormat("numberOfSamples",8*e.chunkSize/this.metadata.format.numberOfChannels),this.metadata.setFormat("duration",this.metadata.format.numberOfSamples/this.metadata.format.sampleRate)}const r=e.chunkSize-(this.tokenizer.position-t);r>0&&(f(`After Parsing chunk, remaining ${r} bytes`),await this.tokenizer.ignore(r))}async handleSoundPropertyChunks(e){for(f(`Parsing sound-property-chunks, remainingSize=${e}`);e>0;){const t=await this.tokenizer.readToken(u.ChunkHeader);f(`Sound-property-chunk[ID=${t.chunkID}, size=${t.chunkSize}]`);const r=this.tokenizer.position;switch(t.chunkID.trim()){case"FS":const e=await this.tokenizer.readToken(i.UINT32_BE);this.metadata.setFormat("sampleRate",e);break;case"CHNL":const r=await this.tokenizer.readToken(i.UINT16_BE);this.metadata.setFormat("numberOfChannels",r),await this.handleChannelChunks(t.chunkSize-i.UINT16_BE.len);break;case"CMPR":const n=(await this.tokenizer.readToken(s.FourCcToken)).trim(),a=await this.tokenizer.readToken(i.UINT8),o=await this.tokenizer.readToken(new i.StringType(a,"ascii"));"DSD"===n&&(this.metadata.setFormat("lossless",!0),this.metadata.setFormat("bitsPerSample",1)),this.metadata.setFormat("codec",`${n} (${o})`);break;case"ABSS":const c=await this.tokenizer.readToken(i.UINT16_BE),u=await this.tokenizer.readToken(i.UINT8),l=await this.tokenizer.readToken(i.UINT8),d=await this.tokenizer.readToken(i.UINT32_BE);f(`ABSS ${c}:${u}:${l}.${d}`);break;case"LSCO":const p=await this.tokenizer.readToken(i.UINT16_BE);f(`LSCO lsConfig=${p}`);break;case"COMT":default:f(`Unknown sound-property-chunk[ID=${t.chunkID}, size=${t.chunkSize}]`),await this.tokenizer.ignore(t.chunkSize)}const n=t.chunkSize-(this.tokenizer.position-r);n>0&&(f(`After Parsing sound-property-chunk ${t.chunkSize}, remaining ${n} bytes`),await this.tokenizer.ignore(n)),e-=u.ChunkHeader.len+t.chunkSize,f(`Parsing sound-property-chunks, remainingSize=${e}`)}if(this.metadata.format.lossless&&this.metadata.format.sampleRate&&this.metadata.format.numberOfChannels&&this.metadata.format.bitsPerSample){const e=this.metadata.format.sampleRate*this.metadata.format.numberOfChannels*this.metadata.format.bitsPerSample;this.metadata.setFormat("bitrate",e)}}async handleChannelChunks(e){f(`Parsing channel-chunks, remainingSize=${e}`);const t=[];for(;e>=s.FourCcToken.len;){const r=await this.tokenizer.readToken(s.FourCcToken);f(`Channel[ID=${r}]`),t.push(r),e-=s.FourCcToken.len}return f(`Channels: ${t.join(", ")}`),t}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(1),i=r(5);t.ChunkHeader={len:12,get:(e,t)=>({chunkID:i.FourCcToken.get(e,t),chunkSize:n.INT64_BE.get(e,t+4)})}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=r(36);t.ReadableWeToNodeStream=class extends n.Readable{constructor(e){super(),this.bytesRead=0,this.released=!1,this.reader=e.getReader()}async _read(){if(this.released)return void this.push(null);this.pendingRead=this.reader.read();const e=await this.pendingRead;delete this.pendingRead,e.done||this.released?this.push(null):(this.bytesRead+=e.value.length,this.push(e.value))}async waitForReadToComplete(){this.pendingRead&&await this.pendingRead}async close(){await this.syncAndRelease()}async syncAndRelease(){this.released=!0,await this.waitForReadToComplete(),await this.reader.releaseLock()}}},function(e,t,r){(function(t){var n=r(272).strict;e.exports=function(e){if(n(e)){var r=t.from(e.buffer);return e.byteLength!==e.buffer.byteLength&&(r=r.slice(e.byteOffset,e.byteOffset+e.byteLength)),r}return t.from(e)}}).call(this,r(3).Buffer)},function(e,t){e.exports=i,i.strict=a,i.loose=s;var r=Object.prototype.toString,n={"[object Int8Array]":!0,"[object Int16Array]":!0,"[object Int32Array]":!0,"[object Uint8Array]":!0,"[object Uint8ClampedArray]":!0,"[object Uint16Array]":!0,"[object Uint32Array]":!0,"[object Float32Array]":!0,"[object Float64Array]":!0};function i(e){return a(e)||s(e)}function a(e){return e instanceof Int8Array||e instanceof Int16Array||e instanceof Int32Array||e instanceof Uint8Array||e instanceof Uint8ClampedArray||e instanceof Uint16Array||e instanceof Uint32Array||e instanceof Float32Array||e instanceof Float64Array}function s(e){return n[r.call(e)]}}]); \ No newline at end of file diff --git a/readme.txt b/readme.txt index e57698e0..c1df8c3a 100755 --- a/readme.txt +++ b/readme.txt @@ -5,7 +5,7 @@ Requires at least: 4.4 Tested up to: 5.3 License: GPLv3 or later License URI: http://www.gnu.org/licenses/gpl-3.0.html -Stable tag: 3.3.11 +Stable tag: 3.3.12 Requires PHP: 5.6.4 Automatically store media on Amazon S3, Google Cloud Storage, DigitalOcean Spaces + others. Serve CSS/JS assets through CDNs. Integrate with Imgix. @@ -108,6 +108,15 @@ No, I'm just one very enthusiastic customer. == Changelog == += 3.3.12 = + +* Fix for non-image uploads when you have the WordPress setting "" turned off but no upload path set in Cloud Storage Settings. If you were having problems with videos, PDF's, etc. this should fix it. +* Performance fix for sites with huge post tables that were seeing slow page performance. +* Fix for IPTC parsing that contains binary data +* Fix for a library conflict +* Warning that the built-in Dynamic Images functionality will be deprecated in the next major version +* Warning about library incompatibility with TranslatePress + = 3.3.11 = * Added us-east-2 region for Wasabi