Initial release
This commit is contained in:
parent
23509c3816
commit
73d5c3335f
13 changed files with 2989 additions and 0 deletions
49
src/cli.js
Normal file
49
src/cli.js
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
const debugModule = require('debug');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const program = require('caporal');
|
||||
const scan = require('./scan');
|
||||
const organise = require('./organise');
|
||||
const generate = require('./generate');
|
||||
|
||||
const debug = debugModule('publikator:cli');
|
||||
const packageJson = require('../package.json');
|
||||
|
||||
process.on('unhandledRejection', error => {
|
||||
throw error;
|
||||
});
|
||||
|
||||
program.version(packageJson.version);
|
||||
|
||||
/* ~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^
|
||||
* Command: organise
|
||||
* ~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^ */
|
||||
|
||||
program
|
||||
.command(
|
||||
'organise',
|
||||
'Recursively finds all mp3s in a folder, reads their tags and re-organises them'
|
||||
)
|
||||
.argument('<source>', 'Root folder for the recursive search')
|
||||
.argument('<target>', 'Target folder for the restructured output')
|
||||
.option('-d, --delete', 'Completely delete the target folder first')
|
||||
.action(async (args, options) => {
|
||||
if (!process.env.DEBUG) {
|
||||
debugModule.enable('publikator:*');
|
||||
}
|
||||
const source = path.resolve(args.source);
|
||||
const target = path.resolve(args.target);
|
||||
if (options.delete) {
|
||||
debug(`deleting folder '${target}'`);
|
||||
fs.removeSync(target);
|
||||
}
|
||||
fs.ensureDirSync(args.target);
|
||||
const files = scan.findFilesSync(source);
|
||||
const filesWithTags = await scan.readTags(files);
|
||||
const organisedFiles = await organise.byAlbum(target, filesWithTags);
|
||||
const releaseInfo = generate.releaseInfo(organisedFiles);
|
||||
fs.writeFileSync(path.resolve(target, 'releases.yml'), releaseInfo);
|
||||
});
|
||||
|
||||
debug(process.argv);
|
||||
program.parse(process.argv);
|
||||
39
src/generate.js
Normal file
39
src/generate.js
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
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;
|
||||
}, {});
|
||||
|
||||
module.exports = {
|
||||
releaseInfo: files => {
|
||||
debug(`generating release info for ${files.length} file(s)`);
|
||||
const albums = _.groupBy(files, file => path.dirname(file.path));
|
||||
return yaml.safeDump(
|
||||
Object.keys(albums).map(key => {
|
||||
const tracks = albums[key];
|
||||
return {
|
||||
'track-count': tracks.length,
|
||||
tracks: tracks.map((track, i) => ({
|
||||
path: track.path,
|
||||
size: track.size,
|
||||
position: i,
|
||||
tags: getTags(track.tags, [
|
||||
'title',
|
||||
'artist',
|
||||
'album',
|
||||
'year',
|
||||
'comment',
|
||||
'track',
|
||||
'genre',
|
||||
]),
|
||||
})),
|
||||
};
|
||||
})
|
||||
);
|
||||
},
|
||||
};
|
||||
55
src/organise.js
Normal file
55
src/organise.js
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
const fs = require('fs-extra');
|
||||
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 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',
|
||||
]);
|
||||
|
||||
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))))
|
||||
);
|
||||
|
||||
debug(`copying tracks`);
|
||||
return Promise.all(
|
||||
files.map(file => {
|
||||
const newPath = path.resolve(
|
||||
root,
|
||||
getFolderName(file),
|
||||
getFileName(file)
|
||||
);
|
||||
fs.copyFileSync(file.path, newPath);
|
||||
return _.assign({}, file, { path: newPath });
|
||||
})
|
||||
);
|
||||
},
|
||||
};
|
||||
44
src/scan.js
Normal file
44
src/scan.js
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
const debug = require('debug')('publikator:scan');
|
||||
const jsmediatags = require('jsmediatags');
|
||||
const walk = require('walkdir');
|
||||
|
||||
module.exports = {
|
||||
findFilesSync: (root, extension = '.mp3') => {
|
||||
debug(
|
||||
`scanning directory '${root}' for files with extension '${extension}'`
|
||||
);
|
||||
const files = [];
|
||||
walk.sync(root, path => {
|
||||
if (path.endsWith(extension)) {
|
||||
files.push(path);
|
||||
}
|
||||
});
|
||||
debug(`found ${files.length} file(s)`);
|
||||
return files;
|
||||
},
|
||||
|
||||
readTags: files => {
|
||||
debug(`reading tags for ${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);
|
||||
},
|
||||
});
|
||||
})
|
||||
)
|
||||
);
|
||||
},
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue