mudl/index.js

172 lines
5.0 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],
album: pAlbum || null,
artist: pArtist || null,
title: pTitle || null,
}];
} 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}"`);
console.log(`Finished: ${album}/${artist} - ${title}`);
}
console.log('Finished everything, cleaning up...');
await fs.rmdir(TMP_DIR, {recursive: true});
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();
});