Refactor and add documentation
This commit is contained in:
parent
73d5c3335f
commit
09fa089cfe
|
@ -1,5 +1,5 @@
|
|||
parserOptions:
|
||||
ecmaVersion: 2017
|
||||
ecmaVersion: 2018
|
||||
sourceType: module
|
||||
env:
|
||||
node: true
|
||||
|
|
|
@ -39,8 +39,8 @@ program
|
|||
}
|
||||
fs.ensureDirSync(args.target);
|
||||
const files = scan.findFilesSync(source);
|
||||
const filesWithTags = await scan.readTags(files);
|
||||
const organisedFiles = await organise.byAlbum(target, filesWithTags);
|
||||
const taggedFiles = await scan.readTags(files);
|
||||
const organisedFiles = await organise.byAlbum(target, taggedFiles);
|
||||
const releaseInfo = generate.releaseInfo(organisedFiles);
|
||||
fs.writeFileSync(path.resolve(target, 'releases.yml'), releaseInfo);
|
||||
});
|
||||
|
|
|
@ -2,17 +2,15 @@ const path = require('path');
|
|||
const _ = require('lodash');
|
||||
const yaml = require('js-yaml');
|
||||
const debug = require('debug')('publikator:generate');
|
||||
|
||||
const getTags = (track, tags) =>
|
||||
tags.reduce((all, tag) => {
|
||||
all[tag] = track[tag]; // eslint-disable-line
|
||||
return all;
|
||||
}, {});
|
||||
const tags = require('./tags');
|
||||
|
||||
module.exports = {
|
||||
releaseInfo: files => {
|
||||
debug(`generating release info for ${files.length} file(s)`);
|
||||
const albums = _.groupBy(files, file => path.dirname(file.path));
|
||||
/**
|
||||
* Generates a release YAML with data
|
||||
*/
|
||||
releaseInfo: taggedFiles => {
|
||||
debug(`generating release info for ${taggedFiles.length} file(s)`);
|
||||
const albums = _.groupBy(taggedFiles, file => path.dirname(file.path));
|
||||
return yaml.safeDump(
|
||||
Object.keys(albums).map(key => {
|
||||
const tracks = albums[key];
|
||||
|
@ -22,7 +20,7 @@ module.exports = {
|
|||
path: track.path,
|
||||
size: track.size,
|
||||
position: i,
|
||||
tags: getTags(track.tags, [
|
||||
...tags.getTags(track, [
|
||||
'title',
|
||||
'artist',
|
||||
'album',
|
||||
|
|
|
@ -3,31 +3,27 @@ const path = require('path');
|
|||
const _ = require('lodash');
|
||||
const sanitize = require('sanitize-filename');
|
||||
const debug = require('debug')('publikator:organise');
|
||||
|
||||
const ensureTags = (files, tags) => {
|
||||
return files.filter(file => {
|
||||
if (tags.some(tag => file.tags[tag] === undefined)) {
|
||||
debug(
|
||||
`ignored '${file.path}' because it is missing one or more required tags`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
};
|
||||
const tags = require('./tags');
|
||||
|
||||
const getFolderName = file => `${file.tags.artist} - ${file.tags.album}`;
|
||||
const getFileName = file =>
|
||||
`${file.tags.track} - ${file.tags.title}${path.extname(file.path)}`;
|
||||
|
||||
module.exports = {
|
||||
byAlbum: async (root, filesWithTags) => {
|
||||
const files = ensureTags(filesWithTags, [
|
||||
'artist',
|
||||
'album',
|
||||
'track',
|
||||
'title',
|
||||
]);
|
||||
/**
|
||||
* 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 =>
|
||||
tags.hasTags(file, ['artist', 'album', 'track', 'title'])
|
||||
);
|
||||
|
||||
debug(`grouping tracks by album`);
|
||||
const folders = _.uniq(files.map(file => getFolderName(file)));
|
||||
|
@ -41,13 +37,13 @@ module.exports = {
|
|||
|
||||
debug(`copying tracks`);
|
||||
return Promise.all(
|
||||
files.map(file => {
|
||||
files.map(async file => {
|
||||
const newPath = path.resolve(
|
||||
root,
|
||||
getFolderName(file),
|
||||
getFileName(file)
|
||||
);
|
||||
fs.copyFileSync(file.path, newPath);
|
||||
await fs.copyFile(file.path, newPath);
|
||||
return _.assign({}, file, { path: newPath });
|
||||
})
|
||||
);
|
||||
|
|
38
src/scan.js
38
src/scan.js
|
@ -1,8 +1,11 @@
|
|||
const debug = require('debug')('publikator:scan');
|
||||
const jsmediatags = require('jsmediatags');
|
||||
const walk = require('walkdir');
|
||||
const tags = require('./tags');
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* Recursively searches for files.
|
||||
*/
|
||||
findFilesSync: (root, extension = '.mp3') => {
|
||||
debug(
|
||||
`scanning directory '${root}' for files with extension '${extension}'`
|
||||
|
@ -17,28 +20,21 @@ module.exports = {
|
|||
return files;
|
||||
},
|
||||
|
||||
/**
|
||||
* Reads ID3 tags from all files and returns an array in the form of:
|
||||
* [{ path, size, tags }, ...]
|
||||
*/
|
||||
readTags: files => {
|
||||
debug(`reading tags for ${files.length} file(s)`);
|
||||
debug(`reading tags from ${files.length} file(s)`);
|
||||
return Promise.all(
|
||||
files.map(
|
||||
file =>
|
||||
new Promise((resolve, reject) => {
|
||||
jsmediatags.read(file, {
|
||||
onSuccess: info => {
|
||||
resolve({
|
||||
path: file,
|
||||
size: info.size,
|
||||
tags: info.tags,
|
||||
});
|
||||
},
|
||||
onError: error => {
|
||||
debug(error.type);
|
||||
debug(error.info);
|
||||
reject(error);
|
||||
},
|
||||
});
|
||||
})
|
||||
)
|
||||
files.map(async file => {
|
||||
const info = await tags.readTags(file);
|
||||
return {
|
||||
path: file,
|
||||
size: info.size,
|
||||
tags: info.tags,
|
||||
};
|
||||
})
|
||||
);
|
||||
},
|
||||
};
|
||||
|
|
43
src/tags.js
Normal file
43
src/tags.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
const jsmediatags = require('jsmediatags');
|
||||
const debug = require('debug')('publikator:tags');
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* Reads tags from a track.
|
||||
*/
|
||||
readTags: file =>
|
||||
new Promise((resolve, reject) => {
|
||||
jsmediatags.read(file, {
|
||||
onSuccess: info => {
|
||||
resolve(info);
|
||||
},
|
||||
onError: error => {
|
||||
debug(error.type);
|
||||
debug(error.info);
|
||||
reject(error);
|
||||
},
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Returns true if a file has all required tags.
|
||||
*/
|
||||
hasTags: (taggedFile, tags) => {
|
||||
if (tags.some(tag => taggedFile.tags[tag] === undefined)) {
|
||||
debug(`track'${taggedFile.path}' is missing one or more required tags`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Extracts tags from a file into an object, ignoring missing tags.
|
||||
*/
|
||||
getTags: (taggedFile, tags) =>
|
||||
tags.reduce((all, tag) => {
|
||||
if (taggedFile.tags[tag] !== undefined) {
|
||||
all[tag] = taggedFile.tags[tag]; // eslint-disable-line
|
||||
}
|
||||
return all;
|
||||
}, {}),
|
||||
};
|
Loading…
Reference in a new issue