2018-08-14 18:26:02 +02:00
|
|
|
const fs = require('fs-extra');
|
|
|
|
const path = require('path');
|
|
|
|
const _ = require('lodash');
|
|
|
|
const sanitize = require('sanitize-filename');
|
|
|
|
const debug = require('debug')('publikator:organise');
|
2018-08-20 15:18:47 +02:00
|
|
|
const mime = require('mime-types');
|
2018-08-17 15:48:56 +02:00
|
|
|
const tags = require('./tags');
|
2018-08-14 18:26:02 +02:00
|
|
|
|
2018-08-20 14:35:21 +02:00
|
|
|
const getFolderName = file => file.common.album.replace(/ /g, '_');
|
2018-08-20 15:18:47 +02:00
|
|
|
|
2018-08-14 18:26:02 +02:00
|
|
|
const getFileName = file =>
|
2018-08-20 14:35:21 +02:00
|
|
|
`${file.common.track.no}-${file.common.title}${path.extname(
|
|
|
|
file.path
|
|
|
|
)}`.replace(/ /g, '_');
|
2018-08-14 18:26:02 +02:00
|
|
|
|
2018-08-20 15:18:47 +02:00
|
|
|
/**
|
|
|
|
* Extracts the cover art and saves it to a file with the same name.
|
|
|
|
*/
|
|
|
|
const extractCoverArt = async filePath => {
|
|
|
|
const pictures = await tags.extractCoverArt(filePath);
|
|
|
|
if (pictures) {
|
2018-08-20 18:42:59 +02:00
|
|
|
const picture = pictures[0];
|
|
|
|
const pictureExt = mime.extension(picture.format);
|
|
|
|
const picturePath = `${filePath.replace(
|
|
|
|
path.extname(filePath),
|
|
|
|
''
|
|
|
|
)}.${pictureExt}`;
|
|
|
|
await fs.writeFile(picturePath, picture.data);
|
|
|
|
return picturePath;
|
2018-08-20 15:18:47 +02:00
|
|
|
}
|
2018-08-20 18:42:59 +02:00
|
|
|
return null;
|
2018-08-20 15:18:47 +02:00
|
|
|
};
|
|
|
|
|
2018-08-14 18:26:02 +02:00
|
|
|
module.exports = {
|
2018-08-17 15:48:56 +02:00
|
|
|
/**
|
|
|
|
* Organises tracks into a new folder structure in `root`, as follows:
|
|
|
|
*
|
|
|
|
* {artist} - {album}/
|
|
|
|
* {track} - {title}.{ext}
|
|
|
|
* {track} - {title}.{ext}
|
|
|
|
* ...
|
|
|
|
*
|
|
|
|
* Returns `taggedFiles` with the paths changed to the new paths.
|
|
|
|
*/
|
|
|
|
byAlbum: async (root, taggedFiles) => {
|
|
|
|
const files = taggedFiles.filter(file =>
|
2018-08-17 18:53:14 +02:00
|
|
|
tags.hasTags(file, [
|
|
|
|
'common.artists',
|
|
|
|
'common.album',
|
|
|
|
'common.track',
|
|
|
|
'common.title',
|
|
|
|
])
|
2018-08-17 15:48:56 +02:00
|
|
|
);
|
2018-08-14 18:26:02 +02:00
|
|
|
|
|
|
|
debug(`grouping tracks by album`);
|
|
|
|
const folders = _.uniq(files.map(file => getFolderName(file)));
|
|
|
|
debug(`found ${folders.length} album(s)`);
|
|
|
|
debug(folders);
|
|
|
|
|
|
|
|
debug(`creating album directories`);
|
|
|
|
await Promise.all(
|
|
|
|
folders.map(album => fs.ensureDir(path.resolve(root, sanitize(album))))
|
|
|
|
);
|
|
|
|
|
2018-08-20 15:18:47 +02:00
|
|
|
debug(`copying tracks & extracting covers`);
|
2018-08-14 18:26:02 +02:00
|
|
|
return Promise.all(
|
2018-08-17 15:48:56 +02:00
|
|
|
files.map(async file => {
|
2018-08-20 14:35:21 +02:00
|
|
|
const folderName = getFolderName(file);
|
|
|
|
const fileName = getFileName(file);
|
|
|
|
const newPath = path.resolve(root, folderName, fileName);
|
2018-08-17 15:48:56 +02:00
|
|
|
await fs.copyFile(file.path, newPath);
|
2018-08-20 18:42:59 +02:00
|
|
|
const coverPath = await extractCoverArt(newPath);
|
2018-08-20 14:35:21 +02:00
|
|
|
return _.assign(
|
|
|
|
{},
|
|
|
|
{
|
|
|
|
path: newPath,
|
|
|
|
relativePath: `${folderName}/${fileName}`,
|
|
|
|
folderName,
|
|
|
|
fileName,
|
|
|
|
},
|
2018-08-20 18:42:59 +02:00
|
|
|
coverPath
|
|
|
|
? {
|
|
|
|
coverPath,
|
|
|
|
relativeCoverPath: `${folderName}/${path.basename(coverPath)}`,
|
|
|
|
coverFileName: path.basename(coverPath),
|
|
|
|
}
|
|
|
|
: {},
|
2018-08-20 14:35:21 +02:00
|
|
|
_.omit(file, 'path')
|
|
|
|
);
|
2018-08-14 18:26:02 +02:00
|
|
|
})
|
|
|
|
);
|
|
|
|
},
|
|
|
|
};
|