<commit_before>var fs = require('fs');
var Datastore = require('nedb');
var mm = require('musicmetadata');
var walk = require('walk');
var Song = require('./song');

function Database() {
  this.isOpen = false;
  this.path = null;

  this.library = null;
  this.albums = null;
  this.artists = null;
}

Database.prototype.open = function(path, callback) {
  this.isOpen = false;

  var dbDirectoryPath = path + '/.oudjo';

  // Create database directory if it doesn't exist
  try {
    fs.statSync(dbDirectoryPath);
  } catch (err) {
    fs.mkdirSync(dbDirectoryPath);
    fs.mkdirSync(dbDirectoryPath + '/cache');
  }

  this.path = path;
  this.library = new Datastore(path + '/.oudjo/library.db');
  this.albums = new Datastore(path + '/.oudjo/albums.db');
  this.artists = new Datastore(path + '/.oudjo/artists.db');

  var databasesToLoad = 3;
  var onDatabaseError = function(err, dbname) {
    if (err) {
      console.error(`Error reading ${dbname} database`);

      if (callback) {
        callback(err);
      }
    } else {
      console.log(`Loaded ${dbname} database`);

      databasesToLoad -= 1;

      if (databasesToLoad === 0) {
        this.isOpen = true;

        if (callback) {
          callback();
        }
      }
    }
  }.bind(this);

  this.library.loadDatabase(function(err) {
    onDatabaseError(err, 'library');
  });

  this.albums.loadDatabase(function(err) {
    onDatabaseError(err, 'albums');
  });

  this.artists.loadDatabase(function(err) {
    onDatabaseError(err, 'artists');
  });
};

Database.prototype.scan = function(callback) {
  if (!this.isOpen) {
    throw new Error('Couldn\'t scan database, because it isn\'t open');
  }

  // Count the amount of files in the directory
  var fileCount = 0;
  var walkOptions = {
    listeners: {
      names: function(root, nodeNamesArray) {
        fileCount += nodeNamesArray.length;
      }
    }
  };

  walk.walkSync(this.path, walkOptions);
  console.log(`Files to scan: ${fileCount}`);

  // Scan files
  walkOptions = {
    listeners: {
      file: this._scanOnFile.bind(this),
      end: function() {
        if (callback) {
          callback();
        }
      }
    }
  };
  walk.walk(this.path, walkOptions);
};

Database.prototype.getAlbumArt = function(sid) {
  return new Promise(function(fulfill, reject) {
    this.library.find({ _id: sid }, function(err, docs) {
      if (err) {
        reject(err);
      }

      if (docs.length === 0) {
        reject(new Error('No song with such ID'));
      }

      var song = docs[0];
      mm(fs.createReadStream(this.path + song.path), function(err, metadata) {
        if (err) {
          reject(err);
        }

        if (metadata.picture.length === 0) {
          reject(new Error('Requested song has no album art'));
        } else {
          fulfill(metadata.picture[0]);
        }
      });
    }.bind(this));
  }.bind(this));
};

Database.prototype._scanOnFile = function(root, fileStat, next) {
  var filePath = require('path').resolve(root, fileStat.name);
  var extension = require('path').extname(filePath).toLowerCase();

  if (extension !== '.mp3') {
    next();
    return;
  }

  mm(fs.createReadStream(filePath), function(err, metadata) {
    if (err) {
      console.error(err);
    }

    var relativePath = filePath.slice(this.path.length, filePath.length);

    var song = new Song(
      metadata.title,
      metadata.artist[0],
      metadata.album,
      metadata.year,
      metadata.track.no,
      relativePath
    );

    var addArtistId = function() {
      return new Promise(function(fulfill, reject) {
        this.artists.find({ name: song.artist }, function(err, docs) {
          if (err) {
            reject(err);
          }

          if (docs.length === 1) {
            song.artistId = docs[0]._id;
            fulfill();
          } else {
            this.artists.insert({ name: song.artist }, function(err, newDoc) {
              if (err) {
                reject(err);
              }<commit_msg>Fixed adding broken files to database<commit_after>var fs = require('fs');
var Datastore = require('nedb');
var mm = require('musicmetadata');
var walk = require('walk');
var Song = require('./song');

function Database() {
  this.isOpen = false;
  this.path = null;

  this.library = null;
  this.albums = null;
  this.artists = null;
}

Database.prototype.open = function(path, callback) {
  this.isOpen = false;

  var dbDirectoryPath = path + '/.oudjo';

  // Create database directory if it doesn't exist
  try {
    fs.statSync(dbDirectoryPath);
  } catch (err) {
    fs.mkdirSync(dbDirectoryPath);
    fs.mkdirSync(dbDirectoryPath + '/cache');
  }

  this.path = path;
  this.library = new Datastore(path + '/.oudjo/library.db');
  this.albums = new Datastore(path + '/.oudjo/albums.db');
  this.artists = new Datastore(path + '/.oudjo/artists.db');

  var databasesToLoad = 3;
  var onDatabaseError = function(err, dbname) {
    if (err) {
      console.error(`Error reading ${dbname} database`);

      if (callback) {
        callback(err);
      }
    } else {
      console.log(`Loaded ${dbname} database`);

      databasesToLoad -= 1;

      if (databasesToLoad === 0) {
        this.isOpen = true;

        if (callback) {
          callback();
        }
      }
    }
  }.bind(this);

  this.library.loadDatabase(function(err) {
    onDatabaseError(err, 'library');
  });

  this.albums.loadDatabase(function(err) {
    onDatabaseError(err, 'albums');
  });

  this.artists.loadDatabase(function(err) {
    onDatabaseError(err, 'artists');
  });
};

Database.prototype.scan = function(callback) {
  if (!this.isOpen) {
    throw new Error('Couldn\'t scan database, because it isn\'t open');
  }

  // Count the amount of files in the directory
  var fileCount = 0;
  var walkOptions = {
    listeners: {
      names: function(root, nodeNamesArray) {
        fileCount += nodeNamesArray.length;
      }
    }
  };

  walk.walkSync(this.path, walkOptions);
  console.log(`Files to scan: ${fileCount}`);

  // Scan files
  walkOptions = {
    listeners: {
      file: this._scanOnFile.bind(this),
      end: function() {
        if (callback) {
          callback();
        }
      }
    }
  };
  walk.walk(this.path, walkOptions);
};

Database.prototype.getAlbumArt = function(sid) {
  return new Promise(function(fulfill, reject) {
    this.library.find({ _id: sid }, function(err, docs) {
      if (err) {
        reject(err);
      }

      if (docs.length === 0) {
        reject(new Error('No song with such ID'));
      }

      var song = docs[0];
      mm(fs.createReadStream(this.path + song.path), function(err, metadata) {
        if (err) {
          reject(err);
        }

        if (metadata.picture.length === 0) {
          reject(new Error('Requested song has no album art'));
        } else {
          fulfill(metadata.picture[0]);
        }
      });
    }.bind(this));
  }.bind(this));
};

Database.prototype._scanOnFile = function(root, fileStat, next) {
  var filePath = require('path').resolve(root, fileStat.name);
  var extension = require('path').extname(filePath).toLowerCase();

  if (extension !== '.mp3') {
    next();
    return;
  }

  mm(fs.createReadStream(filePath), function(err, metadata) {
    if (err) {
      console.error(err);
    }

    var relativePath = filePath.slice(this.path.length, filePath.length);

    if (metadata.title === '' || metadata.artist.length === 0 || metadata.album === '') {
      next();
      return;
    }

    var song = new Song(
      metadata.title,
      metadata.artist[0],
      metadata.album,
      metadata.year,
      metadata.track.no,
      relativePath
    );

    var addArtistId = function() {
      return new Promise(function(fulfill, reject) {
        this.artists.find({ name: song.artist }, function(err, docs) {
          if (err) {
            reject(err);
          }

          if (docs.length === 1) {
            song.artistId = docs[0]._id;
            fulfill();
          } else {
            this.artists.insert({ name: song.artist }, function(err, newDoc) {
              if (err) {
                reject(err);
              }
{'fraction_non_alphanumeric': 0.15092717671619796, 'fraction_numerical': 0.003070121576814442, 'mean_word_length': 2.4004175365344467, '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': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1026112', 'n_tokens_mistral': 2646, 'n_tokens_neox': 2566, 'n_words': 548}