182 lines
5.3 KiB
JavaScript
182 lines
5.3 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/* eslint-disable no-console */
|
|
|
|
const fs = require('fs').promises;
|
|
const os = require('os');
|
|
const shitExec = require('child_process').exec;
|
|
const Path = require('path');
|
|
|
|
async function exec(...args) {
|
|
return new Promise((resolve, reject) => {
|
|
shitExec(...args, (error, stdout) => {
|
|
if (error) {
|
|
reject(error);
|
|
} else {
|
|
resolve(stdout.trim());
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
const TMP_DIR = `/tmp/mudl_${Math.random().toString(36).substring(7)}`;
|
|
|
|
async function thing() {
|
|
if (!process.argv[2]) {
|
|
console.log('need a video');
|
|
return;
|
|
}
|
|
|
|
const [pAlbum, pArtist, pTitle] = (process.argv.length >= 3 ? process.argv.slice(3).join(' ').trim().match(/^([^/]+)(?:\/([^/]+)(?:\/([^/]+))?)?$/) || [] : []).slice(1).map((item) => {
|
|
return item ? item.trim() : item;
|
|
});
|
|
|
|
await fs.mkdir(TMP_DIR);
|
|
|
|
let files = [];
|
|
let inputType = null;
|
|
|
|
if (['/', '~', '.'].includes(process.argv[2][0])) {
|
|
console.log('got a str8 filename');
|
|
|
|
inputType = 'file';
|
|
|
|
files = [{
|
|
image: null,
|
|
file: process.argv[2],
|
|
}];
|
|
} else {
|
|
console.log('got a link');
|
|
inputType = 'url';
|
|
|
|
console.log(`Temporarily saving to ${TMP_DIR}`);
|
|
await exec(`youtube-dl --write-thumbnail -f bestaudio -x -o "${TMP_DIR}/[%(album)s] -- [%(artist)s] -- [%(track_number)s] -- [%(track)s] -- [%(title)s].%(ext)s" ${process.argv[2]}`);
|
|
console.log('Done saving');
|
|
|
|
const filesList = await fs.readdir(TMP_DIR);
|
|
|
|
files = filesList.map((rawFilePath) => {
|
|
const filePath = Path.parse(rawFilePath);
|
|
|
|
if (['.png', '.jpg', '.webp'].includes(filePath.ext)) return null;
|
|
|
|
const rawImagePath = filesList.find((rawP) => {
|
|
const p = Path.parse(rawP);
|
|
return ['.png', '.jpg', '.webp'].includes(p.ext) && p.name === filePath.name;
|
|
});
|
|
|
|
let album = pAlbum || null;
|
|
let artist = pArtist || null;
|
|
let title = pTitle || null;
|
|
let trackNum = null;
|
|
|
|
let rawTitle = null;
|
|
|
|
const fullMatch = rawFilePath.match(/^\[(.*?)\] -- \[(.*?)\] -- \[(.*?)\] -- \[(.*?)\] -- \[(.*?)\]\..*?$/);
|
|
|
|
if (fullMatch !== null) {
|
|
const [yAlbum, yArtist, yTrackNum, yTitle, yRawTitle] = fullMatch.slice(1)
|
|
.map(item => (item === 'NA' ? null : item))
|
|
.map(item => (item !== null ? item.trim() : null))
|
|
.map(item => (item !== null ? item.replace(/(^|\s+)(.)_($|\s+)/g, '$1$2$3') : null));
|
|
|
|
if (yAlbum && !album) album = yAlbum;
|
|
if (yArtist && !artist) artist = yArtist;
|
|
if (yTitle && !title) title = yTitle;
|
|
if (yTrackNum && !trackNum) trackNum = yTrackNum;
|
|
if (yRawTitle && !rawTitle) rawTitle = yRawTitle;
|
|
}
|
|
|
|
if (artist === null && title === null) {
|
|
const rawTitleMatch = rawTitle.match(/^([^-]+)(?:\s*-\s*(.+))?$/);
|
|
|
|
if (rawTitleMatch[2] !== undefined && rawTitleMatch[2] !== null) {
|
|
[artist, title] = rawTitleMatch.slice(1).map(item => (item ? item.trim() : item));
|
|
} else {
|
|
title = rawTitleMatch[1].trim();
|
|
}
|
|
}
|
|
|
|
if (album === null) album = 'Unknown';
|
|
if (artist === null) artist = 'Unknown';
|
|
if (title === null) title = 'Unknown';
|
|
|
|
return {
|
|
file: Path.join(TMP_DIR, rawFilePath),
|
|
image: Path.join(TMP_DIR, rawImagePath),
|
|
album,
|
|
artist,
|
|
title,
|
|
};
|
|
}).filter(p => p !== null);
|
|
}
|
|
|
|
for (const {
|
|
album, artist, title, trackNum, file, image,
|
|
} of files) {
|
|
const path = `${os.homedir()}/Music/${album}/${artist} - ${title}.mp3`;
|
|
try {
|
|
await fs.mkdir(`${os.homedir()}/Music/${album}`, { recursive: true });
|
|
} catch (err) { }
|
|
|
|
try {
|
|
await fs.access(path);
|
|
console.log("Path already exists..?");
|
|
} catch (err) {
|
|
console.log(`Encoding to: ${path}`);
|
|
|
|
if (image) {
|
|
await exec(`ffmpeg -i "${file}" -i "${image}" -map 0:0 -map 1:0 -id3v2_version 4 -metadata:s:v title="Album cover" -metadata:s:v comment="Cover (front)" "${path}"`);
|
|
} else {
|
|
await exec(`ffmpeg -i "${file}" -id3v2_version 4 "${path}"`);
|
|
}
|
|
}
|
|
|
|
console.log('Adding id3v2 tags');
|
|
|
|
const tagArgs = [];
|
|
|
|
tagArgs.push(['--artist', artist || 'Unknown']);
|
|
tagArgs.push(['--album', album || 'Unknown']);
|
|
tagArgs.push(['--song', title || 'Unknown']);
|
|
if (inputType === 'url') tagArgs.push(['--comment', process.argv[2]]);
|
|
if (trackNum) tagArgs.push(['--track', trackNum]);
|
|
|
|
const tagArgsString = tagArgs.map(([tag, val]) => `${tag} "${val}"`).join(' ');
|
|
await exec(`id3v2 ${tagArgsString} "${path}"`);
|
|
|
|
if (await fs.exists(Path.join(os.homedir(), '.tmsu'))) {
|
|
console.log('adding tmsu tags');
|
|
|
|
const fsTagArgs = [];
|
|
|
|
if (artist) fsTagArgs.push(['artist', artist]);
|
|
if (album) fsTagArgs.push(['album', album]);
|
|
|
|
if (fsTagArgs.length > 0) {
|
|
const fsTagArgsString = fsTagArgs.map(([tag, val]) => `"${tag}=${val}"`).join(' ');
|
|
await exec(`tmsu tag "${path}" ${fsTagArgsString}`);
|
|
}
|
|
}
|
|
|
|
console.log(`Finished: ${album}/${artist} - ${title}`);
|
|
}
|
|
|
|
console.log('Finished everything, cleaning up...');
|
|
await dir.cleanup();
|
|
console.log('Done!');
|
|
}
|
|
|
|
(async () => {
|
|
try {
|
|
await thing();
|
|
} catch (err) {
|
|
console.error(err);
|
|
await fs.rmdir(TMP_DIR, { recursive: true });
|
|
}
|
|
})();
|
|
|
|
process.on('SIGINT', async () => {
|
|
await fs.rmdir(TMP_DIR, { recursive: true });
|
|
process.exit();
|
|
}); |