Source: lib/metadata/metadata.js

/*! @license
 * Shaka Player
 * Copyright 2026 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

goog.provide('shaka.metadata.Metadata');


/**
 * @export
 */
shaka.metadata.Metadata = class {
  /**
   * Returns all metadata frames found in the media data for the given MIME
   * type.
   *
   * The method invokes every parser factory that has been registered for
   * {@code mimeType} via {@link shaka.metadata.Metadata.registerParserByMime},
   * collects all returned frames, and deduplicates them so that when multiple
   * frames share the same {@code key} only the first occurrence is kept.
   *
   * The same MIME type may have more than one parser registered (e.g. both an
   * ID3v2 and an ID3v1 parser for {@code audio/mpeg}).  All registered parsers
   * are run in registration order and their results are concatenated before
   * deduplication.
   *
   * @param {!Uint8Array} data
   * @param {string} mimeType
   * @return {!Array<!shaka.extern.MetadataFrame>}
   * @export
   */
  static getMetadataFrames(data, mimeType) {
    const factories =
        shaka.metadata.Metadata.parsersByMime_.get(mimeType) || [];

    const frames = [];
    for (const factory of factories) {
      frames.push(...factory().parse(data));
    }

    // Deduplicate: keep only the first frame for each key.
    const seen = new Set();
    const result = [];
    for (const f of frames) {
      if (!seen.has(f.key)) {
        seen.add(f.key);
        result.push(f);
      }
    }

    return result;
  }

  /**
   * Returns {@code true} when at least one parser has been registered for
   * {@code mimeType}.
   *
   * @param {string} mimeType
   * @return {boolean}
   * @export
   */
  static supports(mimeType) {
    const factories = shaka.metadata.Metadata.parsersByMime_.get(mimeType);
    return factories != null && factories.length > 0;
  }

  /**
   * Registers a metadata parser factory for a given MIME type.
   *
   * The same MIME type may be registered more than once; each additional call
   * appends the factory to the list of parsers for that MIME type.  When
   * {@link shaka.metadata.Metadata.getMetadataFrames} is called, all
   * registered parsers for the MIME type are invoked in registration order and
   * their frames are merged (with first-occurrence-wins deduplication on
   * {@code key}).
   *
   * @param {string} mimeType
   * @param {shaka.extern.MetadataParser.Factory} parserFactory
   * @export
   */
  static registerParserByMime(mimeType, parserFactory) {
    const map = shaka.metadata.Metadata.parsersByMime_;
    if (!map.has(mimeType)) {
      map.set(mimeType, []);
    }
    map.get(mimeType).push(parserFactory);
  }

  /**
   * Unregisters all parser factories that have been registered for the given
   * MIME type.
   *
   * @param {string} mimeType
   * @export
   */
  static unregisterParserByMime(mimeType) {
    shaka.metadata.Metadata.parsersByMime_.delete(mimeType);
  }
};


/**
 * Maps each MIME type to the ordered list of parser factories registered for
 * it.  A single MIME type may have multiple factories (e.g. ID3v2 + ID3v1 for
 * {@code audio/mpeg}).
 *
 * @type {!Map<string, !Array<shaka.extern.MetadataParser.Factory>>}
 * @private
 */
shaka.metadata.Metadata.parsersByMime_ = new Map();