<commit_before>import {
  FormatId,
  IAudioMetadata, ICommonTagsResult,
  IFormat,
  INativeAudioMetadata,
  INativeTags, IOptions,
  MusicMetadataParser
} from "../index";

import * as _debug from "debug";
import {GenericTagId, IGenericTag, TagType, isSingleton} from "./GenericTagTypes";
import {CombinedTagMapper} from "./CombinedTagMapper";
import {CommonTagMapper} from "./GenericTagMapper";

const debug = _debug("music-metadata:collector");

const TagPriority: TagType[] = ['APEv2', 'vorbis', 'ID3v2.4', 'ID3v2.3', 'ID3v2.2', 'exif', 'asf', 'iTunes MP4', 'ID3v1'];

/**
 * Combines all generic-tag-mappers for each tag type
 */

export interface INativeMetadataCollector {

  /**
   * Only use this for reading
   */
  readonly format: IFormat;

  readonly native: IFormat;

  /**
   * @returns {boolean} true if one or more tags have been found
   */
  hasAny(): boolean;

  setFormat(key: FormatId, value: any);

  addTag(tagType: string, tagId: string, value: any);

}

/**
 * Provided to the parser to uodate the metadata result.
 * Responsible for triggering async updates
 */
export class MetadataCollector implements INativeMetadataCollector {

  public readonly format: IFormat = {
    tagTypes: []
  };

  public readonly native: INativeTags = {};

  public readonly common: ICommonTagsResult = {
    track: {no: null, of: null},
    disk: {no: null, of: null}
  };

  /**
   * Keeps track of origin priority for each mapped id
   */
  private readonly commonOrigin: {
    [id: string]: number;
  } = {};

  /**
   * Maps a tag type to a priority
   */
  private readonly originPriority: {
    [tagType: string]: number;
  } = {};

  private tagMapper = new CombinedTagMapper();

  public constructor(private opts: IOptions) {
    let priority: number = 1;
    for (const tagType of TagPriority) {
      this.originPriority[tagType] = priority++;
    }
    this.originPriority.artificial = 500; // Filled using alternative tags
    this.originPriority.id3v1 = 600; // Consider worst due to field length limit
  }

  /**
   * @returns {boolean} true if one or more tags have been found
   */
  public hasAny() {
    for (const tagType in this.native) {
      return true;
    }
    return false;
  }

  public setFormat(key: string, value: any) {
    debug(`format: ${key} = ${value}`);
    this.format[key] = value;
  }

  public addTag(tagType: TagType, tagId: string, value: any) {
    debug(`tag ${tagType}.${tagId} = ${value}`);
    if (!this.native[tagType]) {
      this.format.tagTypes.push(tagType);
      this.native[tagType] = [];
    }
    this.native[tagType].push({id: tagId, value});

    this.toCommon(tagType, tagId, value);
  }

  public getNativeMetadata(): INativeAudioMetadata {
    return {
      format: this.format,
      native: this.native
    };
  }

  public postMap(tagType: TagType | 'artificial', tag: IGenericTag) {

    // Common tag (alias) found

    // check if we need to do something special with common tag<commit_msg>#125 Send async observer event after a format property has changed.<commit_after>import {
  FormatId,
  IAudioMetadata, ICommonTagsResult,
  IFormat,
  INativeAudioMetadata,
  INativeTags, IOptions,
  MusicMetadataParser
} from "../index";

import * as _debug from "debug";
import {GenericTagId, IGenericTag, TagType, isSingleton} from "./GenericTagTypes";
import {CombinedTagMapper} from "./CombinedTagMapper";
import {CommonTagMapper} from "./GenericTagMapper";

const debug = _debug("music-metadata:collector");

const TagPriority: TagType[] = ['APEv2', 'vorbis', 'ID3v2.4', 'ID3v2.3', 'ID3v2.2', 'exif', 'asf', 'iTunes MP4', 'ID3v1'];

/**
 * Combines all generic-tag-mappers for each tag type
 */

export interface INativeMetadataCollector {

  /**
   * Only use this for reading
   */
  readonly format: IFormat;

  readonly native: IFormat;

  /**
   * @returns {boolean} true if one or more tags have been found
   */
  hasAny(): boolean;

  setFormat(key: FormatId, value: any);

  addTag(tagType: string, tagId: string, value: any);

}

/**
 * Provided to the parser to uodate the metadata result.
 * Responsible for triggering async updates
 */
export class MetadataCollector implements INativeMetadataCollector {

  public readonly format: IFormat = {
    tagTypes: []
  };

  public readonly native: INativeTags = {};

  public readonly common: ICommonTagsResult = {
    track: {no: null, of: null},
    disk: {no: null, of: null}
  };

  /**
   * Keeps track of origin priority for each mapped id
   */
  private readonly commonOrigin: {
    [id: string]: number;
  } = {};

  /**
   * Maps a tag type to a priority
   */
  private readonly originPriority: {
    [tagType: string]: number;
  } = {};

  private tagMapper = new CombinedTagMapper();

  public constructor(private opts: IOptions) {
    let priority: number = 1;
    for (const tagType of TagPriority) {
      this.originPriority[tagType] = priority++;
    }
    this.originPriority.artificial = 500; // Filled using alternative tags
    this.originPriority.id3v1 = 600; // Consider worst due to field length limit
  }

  /**
   * @returns {boolean} true if one or more tags have been found
   */
  public hasAny() {
    for (const tagType in this.native) {
      return true;
    }
    return false;
  }

  public setFormat(key: FormatId, value: any) {
    debug(`format: ${key} = ${value}`);
    this.format[key] = value;

    if (this.opts.observer) {
      this.opts.observer({metadata: this, tag: {type: 'format', id: key, value}});
    }
  }

  public addTag(tagType: TagType, tagId: string, value: any) {
    debug(`tag ${tagType}.${tagId} = ${value}`);
    if (!this.native[tagType]) {
      this.format.tagTypes.push(tagType);
      this.native[tagType] = [];
    }
    this.native[tagType].push({id: tagId, value});

    this.toCommon(tagType, tagId, value);
  }

  public getNativeMetadata(): INativeAudioMetadata {
    return {
      format: this.format,
      native: this.native
    };
  }

  public postMap(tagType: TagType | 'artificial', tag: IGenericTag) {

    // Common tag (alias) found

    // check if we need to do something special with common tag
{'fraction_non_alphanumeric': 0.12969617771969943, 'fraction_numerical': 0.0076772296635086576, 'mean_word_length': 3.3642195295794726, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6936860', 'n_tokens_mistral': 1971, 'n_tokens_neox': 1909, 'n_words': 571}