<commit_before>// tslint:disable:no-if-statement
// tslint:disable:no-let
import { spawn } from 'child_process';
import ffmpeg, { FfprobeData } from 'fluent-ffmpeg';
import fs from 'fs';
import glob from 'glob';
import path from 'path';
import log from 'winston';
import { BinPaths } from './typings';

const checkVideoStream = (_: Error, metadata: FfprobeData): boolean => {
  if (!metadata || !metadata.streams) {
    return false;
  }

  const videoStream = metadata.streams.filter(
    stream => stream.codec_name === 'mjpeg'
  );

  if (videoStream.length) {
    if (metadata.streams.length === 1) {
      throw new Error('Invalid stream length');
    }

    // log.debug('Has cover stream:');
    // log.debug(videoStream[0]);

    return true;
  }

  return false;
};

const findCoverFile = (dirname: string): string => {
  const preferredFilenames = [
    'cover.jpg',
    'cover.jpeg',
    'folder.jpg',
    'folder.jpeg',
    'front.jpg',
    'front.jpeg'
  ];

  const filepaths = glob.sync('**/*.{jpeg,jpg}', { cwd: dirname });

  let found;

  for (const filepath of filepaths) {
    const base = path.basename(filepath).toLowerCase();
    if (preferredFilenames.indexOf(path.basename(base)) > -1) {
      found = filepath;
    }

    if (found) {
      continue;
    }

    found = filepath;
  }

  if (found) {
    const resolved = path.resolve(dirname, found);
    log.debug('Found cover art file: %s', resolved);
    return resolved;
  }

  return '';
};

const embedCoverFile = (
  outputAudio: string,
  coverFile: string,
  binPaths: BinPaths
): Promise<void> => {
  const cwd = path.dirname(outputAudio);

  log.debug(
    'embedCoverFile: %s, %s, %s, %s',
    binPaths.atomicparsley,
    outputAudio,
    `cover=${coverFile} --overWrite`,
    cwd
  );

  return new Promise((resolve, reject) => {
    const atomicparsley = spawn(
      binPaths.atomicparsley,
      [outputAudio, `--artwork=${coverFile}`, '--overWrite'],
      { cwd }
    );

    atomicparsley.stderr.on('data', data => log.debug(data.toString()));

    atomicparsley.on('error', (err: Error) => reject(err));

    atomicparsley.on('exit', (code: any, signal: any) => {
      if (signal) {
        reject(new Error(`atomicparsley was killed with signal ${signal}`));
      } else if (code) {
        reject(new Error(`atomicparsley exited with code ${code}`));
      } else {
        resolve();
      }
    });
  });
};

export function convertAudio(
  source: string,
  destination: string,
  binPaths: BinPaths
): Promise<any> {
  const dirname = path.dirname(source);
  const outputFilename = path
    .basename(source)
    .replace(path.extname(source), '.m4a');
  const outputAudio = path.resolve(destination, outputFilename);
  const outputCover = path.resolve(destination, 'cover.jpg');

  log.debug(outputAudio);

  const command = ffmpeg(source, { logger: log })
    .noVideo()
    .audioCodec('alac')
    .outputOptions('-map 0:0')
    .outputOptions('-movflags')
    .outputOptions('+faststart')
    .addOutput(outputAudio);

  return new Promise((resolve, reject) => {
    let coverFileEmbedded: string;

    command.on('start', cmdline => log.debug('ffmpeg command: %s', cmdline));
    command.on('error', err => reject(err));<commit_msg>don't reject from atomicparsley<commit_after>// tslint:disable:no-if-statement
// tslint:disable:no-let
import { spawn } from 'child_process';
import ffmpeg, { FfprobeData } from 'fluent-ffmpeg';
import fs from 'fs';
import glob from 'glob';
import path from 'path';
import log from 'winston';
import { BinPaths } from './typings';

const checkVideoStream = (_: Error, metadata: FfprobeData): boolean => {
  if (!metadata || !metadata.streams) {
    return false;
  }

  const videoStream = metadata.streams.filter(
    stream => stream.codec_name === 'mjpeg'
  );

  if (videoStream.length) {
    if (metadata.streams.length === 1) {
      throw new Error('Invalid stream length');
    }

    // log.debug('Has cover stream:');
    // log.debug(videoStream[0]);

    return true;
  }

  return false;
};

const findCoverFile = (dirname: string): string => {
  const preferredFilenames = [
    'cover.jpg',
    'cover.jpeg',
    'folder.jpg',
    'folder.jpeg',
    'front.jpg',
    'front.jpeg'
  ];

  const filepaths = glob.sync('**/*.{jpeg,jpg}', { cwd: dirname });

  let found;

  for (const filepath of filepaths) {
    const base = path.basename(filepath).toLowerCase();
    if (preferredFilenames.indexOf(path.basename(base)) > -1) {
      found = filepath;
    }

    if (found) {
      continue;
    }

    found = filepath;
  }

  if (found) {
    const resolved = path.resolve(dirname, found);
    log.debug('Found cover art file: %s', resolved);
    return resolved;
  }

  return '';
};

const embedCoverFile = (
  outputAudio: string,
  coverFile: string,
  binPaths: BinPaths
): Promise<void> => {
  const cwd = path.dirname(outputAudio);

  log.debug(
    'embedCoverFile: %s, %s, %s, %s',
    binPaths.atomicparsley,
    outputAudio,
    `cover=${coverFile} --overWrite`,
    cwd
  );

  return new Promise(resolve => {
    const atomicparsley = spawn(
      binPaths.atomicparsley,
      [outputAudio, `--artwork=${coverFile}`, '--overWrite'],
      { cwd }
    );

    atomicparsley.stderr.on('data', data => log.debug(data.toString()));

    atomicparsley.on('error', (err: Error) => {
      log.debug(`atomicparsley error ${err.message}`);
      resolve();
    });

    atomicparsley.on('exit', (code: any, signal: any) => {
      if (signal) {
        log.debug(`atomicparsley was killed with signal ${signal}`);
      } else if (code) {
        log.debug(`atomicparsley exited with code ${code}`);
      }
      resolve();
    });
  });
};

export function convertAudio(
  source: string,
  destination: string,
  binPaths: BinPaths
): Promise<any> {
  const dirname = path.dirname(source);
  const outputFilename = path
    .basename(source)
    .replace(path.extname(source), '.m4a');
  const outputAudio = path.resolve(destination, outputFilename);
  const outputCover = path.resolve(destination, 'cover.jpg');

  log.debug(outputAudio);

  const command = ffmpeg(source, { logger: log })
    .noVideo()
    .audioCodec('alac')
    .outputOptions('-map 0:0')
    .outputOptions('-movflags')
    .outputOptions('+faststart')
    .addOutput(outputAudio);

  return new Promise((resolve, reject) => {
    let coverFileEmbedded: string;

    command.on('start', cmdline => log.debug('ffmpeg command: %s', cmdline));
    command.on('error', err => reject(err));
{'fraction_non_alphanumeric': 0.16015325670498085, 'fraction_numerical': 0.0018390804597701149, 'mean_word_length': 3.2570123939986955, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 31, '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': '2937742', 'n_tokens_mistral': 2164, 'n_tokens_neox': 2105, 'n_words': 487}