<commit_before>/* songs.js
 * Manages songs.
 */

var winston = require('winston');
var config = require('../../config');
var fs = require('fs');
var fs_ = require('../../utils/fs');
var upload = require('./upload');
var ffmpeg = require('fluent-ffmpeg');
var file_model = require('../../models/file');
var song_model = require('../../models/song');
var queues = require('./queues');

var generateShortName = function(name) {
  return name.toLowerCase()
      .replace(/[\s_\-]+/g, '-')
      .replace(/[^\w\-\.]/g, '');
};

var filenameOfPath = function(path) {
  return path.replace(/^.*[\\\/]/, '');
};

var removeExtension = function(file) {
  return file.replace(/\.[^\.]*$/, '');
};

var changeExtension = function(file, extension) {
  return removeExtension(file) + '.' + extension;
};

exports.addSong = function(path, user, name, callback) {
  if (!fs.existsSync(path)) {
    var err = new Error('Song path does not exist.');
    winston.error(err.message);
    callback(err);
    return;
  }

  var now = new Date();

  var shortname = generateShortName(removeExtension(name));
  var newpath = upload.song_dir + '/' + changeExtension(shortname, 'mp3');
  var screenshots = [];

  var song, song_file, artwork_file;

  var abort = function(err, stderr) {
    if (stderr)
      winston.error(err.message + '\n' + stderr);
    else
      winston.error(err.message);
    callback(null, new Error('Failed to convert file.'));
    if (song) {
      var songid = song.id;
      song.remove(function(err) {
        if (err)
          winston.error('Failed to delete song entity: ' + err.message);
        else
          winston.info('Abort; Deleted song entity: ' + songid);
      });
    }
    if (song_file) {
      var song_fileid = song_file.id;
      song_file.remove(function(err) {
        if (err)
          winston.error('Failed to delete song file entity: ' + err.message);
        else
          winston.info('Abort; Deleted song file entity: ' + song_fileid);
      });
    }
    if (artwork_file) {
      var artwork_fileid = artwork_file.id;
      artwork_file.remove(function(err) {
        if (err)
          winston.error(
              'Failed to delete artwork file entity: ' + err.message);
        else
          winston.info(
              'Abort; Deleted artwork file entity: ' + artwork_fileid);
      });
    }
    fs_.unlink(path, true);
    fs_.unlink(newpath, true);
    screenshots.forEach(function(name) {
      fs_.unlink(upload.artwork_dir + '/' + name, true);
    });
  };

  // Convert file to mp3.
  var proc = new ffmpeg({ source: path })
    .withAudioCodec('libmp3lame')
    .withAudioBitrate(320)
    .toFormat('mp3')
    .saveToFile(newpath, function(stdout, stderr, err) {
      if (err) {
        abort(err, stderr);
        return;
      }

      // Extract album art (if any).
      new ffmpeg({ source: newpath })
        .withSize('800x800')
        .takeScreenshots({
          count: 1
        }, upload.artwork_dir, function(err, filenames) {
          if (err && filenames.length) {
            winston.error('Error extracting album art: ' + err.message);
            filenames.forEach(function(name) {
              fs_.unlink(upload.artwork_dir + '/' + name);
            });
          } else if (!err) {
            screenshots = filenames;
          }

          // Extract metadata.
          ffmpeg.Metadata(newpath, function(data, err) {
            if (!err && !data.durationsec)
              err = new Error('No duration found.');

            if (err) {
              abort(err, null);
              return;
            }
            
            song = new song_model.Song({
              title: data.title || shortname,
              album: data.album,
              artist: data.artist,
              duration: data.durationsec,
              timeUploaded: now,
              uploader: user
            }); 

            song.save(function(err) {
              if (err) {
                abort(err, null);
                return;
              }

              var base = song.id + '-' + generateShortName(song.title);
              var oldnewpath = newpath;
              var songfilename = changeExtension(base, 'mp3');
              newpath = upload.song_dir + '/' + songfilename;
              fs.renameSync(oldnewpath, newpath); 

              song_file = new file_model.File({
                directory: upload.song_path,
                filename: songfilename,
                timeUploaded: now,
                uploader: user
              });<commit_msg>Fixed song uploading; Adds source and uuid.<commit_after>/* songs.js
 * Manages songs.
 */

var winston = require('winston');
var config = require('../../config');
var fs = require('fs');
var fs_ = require('../../utils/fs');
var upload = require('./upload');
var ffmpeg = require('fluent-ffmpeg');
var file_model = require('../../models/file');
var song_model = require('../../models/song');
var queues = require('./queues');
var crypto = require('crypto');

var generateShortName = function(name) {
  return name.toLowerCase()
      .replace(/[\s_\-]+/g, '-')
      .replace(/[^\w\-\.]/g, '');
};

var filenameOfPath = function(path) {
  return path.replace(/^.*[\\\/]/, '');
};

var removeExtension = function(file) {
  return file.replace(/\.[^\.]*$/, '');
};

var changeExtension = function(file, extension) {
  return removeExtension(file) + '.' + extension;
};

exports.addSong = function(path, user, name, callback) {
  if (!fs.existsSync(path)) {
    var err = new Error('Song path does not exist.');
    winston.error(err.message);
    callback(err);
    return;
  }

  var now = new Date();

  var shortname = generateShortName(removeExtension(name));
  var newpath = upload.song_dir + '/' + changeExtension(shortname, 'mp3');
  var screenshots = [];

  var song, song_file, artwork_file;

  var abort = function(err, stderr) {
    if (stderr)
      winston.error(err.message + '\n' + stderr);
    else
      winston.error(err.message);
    callback(null, new Error('Failed to convert file.'));
    if (song) {
      var songid = song.id;
      song.remove(function(err) {
        if (err)
          winston.error('Failed to delete song entity: ' + err.message);
        else
          winston.info('Abort; Deleted song entity: ' + songid);
      });
    }
    if (song_file) {
      var song_fileid = song_file.id;
      song_file.remove(function(err) {
        if (err)
          winston.error('Failed to delete song file entity: ' + err.message);
        else
          winston.info('Abort; Deleted song file entity: ' + song_fileid);
      });
    }
    if (artwork_file) {
      var artwork_fileid = artwork_file.id;
      artwork_file.remove(function(err) {
        if (err)
          winston.error(
              'Failed to delete artwork file entity: ' + err.message);
        else
          winston.info(
              'Abort; Deleted artwork file entity: ' + artwork_fileid);
      });
    }
    fs_.unlink(path, true);
    fs_.unlink(newpath, true);
    screenshots.forEach(function(name) {
      fs_.unlink(upload.artwork_dir + '/' + name, true);
    });
  };

  // Convert file to mp3.
  var proc = new ffmpeg({ source: path })
    .withAudioCodec('libmp3lame')
    .withAudioBitrate(320)
    .toFormat('mp3')
    .saveToFile(newpath, function(stdout, stderr, err) {
      if (err) {
        abort(err, stderr);
        return;
      }

      // Extract album art (if any).
      new ffmpeg({ source: newpath })
        .withSize('800x800')
        .takeScreenshots({
          count: 1
        }, upload.artwork_dir, function(err, filenames) {
          if (err && filenames.length) {
            winston.warn('Failed to extract album art for ' + newpath);
            filenames.forEach(function(name) {
              fs_.unlink(upload.artwork_dir + '/' + name);
            });
          } else if (!err) {
            screenshots = filenames;
          }

          // Extract metadata.
          ffmpeg.Metadata(newpath, function(data, err) {
            if (!err && !data.durationsec)
              err = new Error('No duration found.');

            if (err) {
              abort(err, null);
              return;
            }

            var sha1 = crypto.createHash('sha1');
            sha1.update('upload:' + Math.random());
            var uuid = sha1.digest('hex'); // temporary uuid, until save.
            
            song = new song_model.Song({
              title: data.title || shortname,
              album: data.album,
              artist: data.artist,
              duration: data.durationsec,
              timeUploaded: now,
              source: 'upload',
              uuid: uuid,
              uploader: user
            }); 

            song.save(function(err) {
              if (err) {
                abort(err, null);
                return;
              }

              var base = song.id + '-' + generateShortName(song.title);
              var oldnewpath = newpath;
              var songfilename = changeExtension(base, 'mp3');
              newpath = upload.song_dir + '/' + songfilename;
              fs.renameSync(oldnewpath, newpath); 

              sha1 = crypto.createHash('sha1');
              sha1.update('upload:' + song.id);
              song.uuid = sha1.digest('hex');

              song_file = new file_model.File({
                directory: upload.song_path,
                filename: songfilename,
                timeUploaded: now,
                uploader: user
              });
{'fraction_non_alphanumeric': 0.14475207916622804, 'fraction_numerical': 0.0040004210969575744, 'mean_word_length': 1.9212792127921279, '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': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2928391', 'n_tokens_mistral': 2893, 'n_tokens_neox': 2828, 'n_words': 639}