chore(cli): improve missing values, skipping prompts and overwriting files

This commit is contained in:
Cory Dransfeldt 2025-06-07 11:26:08 -07:00
parent 56e53e2a33
commit 75df36acc3
No known key found for this signature in database
4 changed files with 105 additions and 54 deletions

View file

@ -11,7 +11,7 @@ import { handleExitError } from '../lib/handlers.js';
process.on('unhandledRejection', (err) => handleExitError(err, 'Unhandled rejection')); process.on('unhandledRejection', (err) => handleExitError(err, 'Unhandled rejection'));
process.on('uncaughtException', (err) => handleExitError(err, 'Uncaught exception')); process.on('uncaughtException', (err) => handleExitError(err, 'Uncaught exception'));
program.name('coryd').description('🪄 Run commands, download things and have fun.').version('1.0.0'); program.name('coryd').description('🪄 Run commands, download things and have fun.').version('1.1.0');
program.command('init').description('Initialize CLI and populate required config.').action(initConfig); program.command('init').description('Initialize CLI and populate required config.').action(initConfig);
program.command('run [script]').description('Run site scripts and commands.').action(runRootScript); program.command('run [script]').description('Run site scripts and commands.').action(runRootScript);
program.command('download').description('Download, name and store image assets.').action(downloadAsset); program.command('download').description('Download, name and store image assets.').action(downloadAsset);

View file

@ -17,36 +17,69 @@ const downloadImage = (url, dest) => new Promise((resolve, reject) => {
file.on('finish', () => file.close(resolve)); file.on('finish', () => file.close(resolve));
}).on('error', reject); }).on('error', reject);
}); });
const isValidTMDBUrl = (val) => const isValidTMDBUrl = (val) => /^https:\/\/image\.tmdb\.org\/t\/p\//.test(val) || '❌ Must be a valid TMDB image url';
/^https:\/\/image\.tmdb\.org\/t\/p\//.test(val) || '❌ Must be a valid TMDB image url'; const overwriteImageDownloadPrompt = async (url, finalPath, fileName) => {
await fs.ensureDir(path.dirname(finalPath));
if (await fs.pathExists(finalPath)) {
const { overwrite } = await inquirer.prompt({
type: 'confirm',
name: 'overwrite',
message: `${fileName} already exists. Overwrite?`,
default: false
});
if (!overwrite) {
console.log(`⚠️ Skipped existing file: ${fileName}`);
return;
}
}
await downloadImage(url, finalPath);
console.log(`✅ Saved to: ${finalPath}`);
};
export const downloadWatchingImages = async () => { export const downloadWatchingImages = async () => {
const config = await loadConfig(); const config = await loadConfig();
const { mediaType } = await inquirer.prompt([{ const { mediaType } = await inquirer.prompt({
type: 'list', type: 'list',
name: 'mediaType', name: 'mediaType',
message: 'Movie or a show?', message: 'Movie or a show?',
choices: Object.keys(config.mediaPaths) choices: Object.keys(config.mediaPaths)
}]); });
const { tmdbId } = await inquirer.prompt([{ const { tmdbId } = await inquirer.prompt({
name: 'tmdbId', name: 'tmdbId',
message: 'Enter the TMDB ID:' message: 'Enter the TMDB ID:'
}]); });
if (!tmdbId) {
console.warn('⚠️ TMDB ID is required.')
return;
}
const { posterUrl, backdropUrl } = await inquirer.prompt([{ const { posterUrl, backdropUrl } = await inquirer.prompt([{
name: 'posterUrl', name: 'posterUrl',
message: 'Enter the poster url:', message: 'Enter the poster url:',
validate: isValidTMDBUrl validate: (val) => {
if (!val) return true;
return isValidTMDBUrl(val);
}
}, },
{ {
name: 'backdropUrl', name: 'backdropUrl',
message: 'Enter the backdrop url:', message: 'Enter the backdrop url:',
validate: isValidTMDBUrl validate: (val) => {
if (!val) return true;
return isValidTMDBUrl(val);
}
}]); }]);
const types = [{ type: 'poster', url: posterUrl }, const types = [{ type: 'poster', url: posterUrl }, { type: 'backdrop', url: backdropUrl }];
{ type: 'backdrop', url: backdropUrl }];
for (const { type, url } of types) { for (const { type, url } of types) {
const fileName = `${type}-${tmdbId}.jpg`; if (!url) continue;
const ext = path.extname(new URL(url).pathname) || '.jpg';
const fileName = `${type}-${tmdbId}${ext}`;
const targetSubPath = config.mediaPaths[mediaType][type]; const targetSubPath = config.mediaPaths[mediaType][type];
if (!targetSubPath) { if (!targetSubPath) {
@ -57,20 +90,23 @@ export const downloadWatchingImages = async () => {
const targetDir = path.join(config.storageDir, targetSubPath); const targetDir = path.join(config.storageDir, targetSubPath);
const finalPath = path.join(targetDir, fileName); const finalPath = path.join(targetDir, fileName);
await fs.ensureDir(targetDir); await overwriteImageDownloadPrompt(url, finalPath, fileName);
await downloadImage(url, finalPath);
console.log(`✅ Saved ${type} to: ${finalPath}`);
} }
} }
export const downloadArtistImage = async () => { export const downloadArtistImage = async () => {
const config = await loadConfig(); const config = await loadConfig();
const { artistName, imageUrl } = await inquirer.prompt([{ const { artistName } = await inquirer.prompt({
name: 'artistName', name: 'artistName',
message: 'Enter the artist name:' message: 'Enter the artist name:'
}, });
{
if (!artistName) {
console.warn('⚠️ Artist name is required.')
return;
}
const { imageUrl } = await inquirer.prompt({
name: 'imageUrl', name: 'imageUrl',
message: 'Enter the artist image url:', message: 'Enter the artist image url:',
validate: (val) => { validate: (val) => {
@ -81,29 +117,41 @@ export const downloadArtistImage = async () => {
return '❌ Must be a valid url.'; return '❌ Must be a valid url.';
} }
} }
}]); });
const sanitizedName = sanitizeMediaString(artistName); const sanitizedName = sanitizeMediaString(artistName);
const fileName = `${sanitizedName}.jpg`; const ext = path.extname(new URL(imageUrl).pathname) || '.jpg';
const fileName = `${sanitizedName}${ext}`;
const targetDir = path.join(config.storageDir, 'artists'); const targetDir = path.join(config.storageDir, 'artists');
const finalPath = path.join(targetDir, fileName); const finalPath = path.join(targetDir, fileName);
await fs.ensureDir(targetDir); await overwriteImageDownloadPrompt(imageUrl, finalPath, fileName);
await downloadImage(imageUrl, finalPath);
console.log(`✅ Saved artist image to: ${finalPath}`);
} }
export const downloadAlbumImage = async () => { export const downloadAlbumImage = async () => {
const config = await loadConfig(); const config = await loadConfig();
const { artistName, albumName, imageUrl } = await inquirer.prompt([{
const { artistName } = await inquirer.prompt({
name: 'artistName', name: 'artistName',
message: 'Enter the artist name:' message: 'Enter the artist name:'
}, });
{
if (!artistName) {
console.warn('⚠️ Artist name is required.');
return;
}
const { albumName } = await inquirer.prompt({
name: 'albumName', name: 'albumName',
message: 'Enter the album name:' message: 'Enter the album name:'
}, });
{
if (!albumName) {
console.warn('⚠️ Album name is required.');
return;
}
const { imageUrl } = await inquirer.prompt({
name: 'imageUrl', name: 'imageUrl',
message: 'Enter the album image url:', message: 'Enter the album image url:',
validate: (val) => { validate: (val) => {
@ -114,31 +162,36 @@ export const downloadAlbumImage = async () => {
return '❌ Must be a valid url.'; return '❌ Must be a valid url.';
} }
} }
}]); });
const artistSlug = sanitizeMediaString(artistName); const artistSlug = sanitizeMediaString(artistName);
const albumSlug = sanitizeMediaString(albumName); const albumSlug = sanitizeMediaString(albumName);
const fileName = `${artistSlug}-${albumSlug}.jpg`; const ext = path.extname(new URL(imageUrl).pathname) || '.jpg';
const fileName = `${artistSlug}-${albumSlug}${ext}`;
const targetDir = path.join(config.storageDir, config.albumPath); const targetDir = path.join(config.storageDir, config.albumPath);
const finalPath = path.join(targetDir, fileName); const finalPath = path.join(targetDir, fileName);
await fs.ensureDir(targetDir); await overwriteImageDownloadPrompt(imageUrl, finalPath, fileName);
await downloadImage(imageUrl, finalPath); };
console.log(`✅ Saved album image to: ${finalPath}`);
}
export const downloadBookImage = async () => { export const downloadBookImage = async () => {
const config = await loadConfig(); const config = await loadConfig();
const { isbn, bookTitle, imageUrl } = await inquirer.prompt([{ const { isbn } = await inquirer.prompt({
name: 'isbn', name: 'isbn',
message: 'Enter the ISBN (no spaces):', message: 'Enter the ISBN (no spaces):',
validate: (val) => /^[a-zA-Z0-9-]+$/.test(val) || 'ISBN must contain only letters, numbers, or hyphens' validate: (val) => /^[a-zA-Z0-9-]+$/.test(val) || 'ISBN must contain only letters, numbers, or hyphens'
}, });
{ const { bookTitle } = await inquirer.prompt({
name: 'bookTitle', name: 'bookTitle',
message: 'Enter the book title:' message: 'Enter the book title:'
}, });
{
if (!bookTitle) {
console.warn('⚠️ Book title is required.');
return;
}
const { imageUrl } = await inquirer.prompt({
name: 'imageUrl', name: 'imageUrl',
message: 'Enter the book cover image URL:', message: 'Enter the book cover image URL:',
validate: (val) => { validate: (val) => {
@ -149,26 +202,24 @@ export const downloadBookImage = async () => {
return 'Must be a valid URL'; return 'Must be a valid URL';
} }
} }
}]); });
const titleSlug = sanitizeMediaString(bookTitle); const titleSlug = sanitizeMediaString(bookTitle);
const fileName = `${isbn}-${titleSlug}.jpg`; const ext = path.extname(new URL(imageUrl).pathname) || '.jpg';
const fileName = `${isbn}-${titleSlug}${ext}`;
const targetDir = path.join(config.storageDir, config.bookPath); const targetDir = path.join(config.storageDir, config.bookPath);
const finalPath = path.join(targetDir, fileName); const finalPath = path.join(targetDir, fileName);
await fs.ensureDir(targetDir); await overwriteImageDownloadPrompt(imageUrl, finalPath, fileName);
await downloadImage(imageUrl, finalPath);
console.log(`✅ Saved book cover to: ${finalPath}`);
} }
export const downloadAsset = async () => { export const downloadAsset = async () => {
const { type } = await inquirer.prompt([{ const { type } = await inquirer.prompt({
type: 'list', type: 'list',
name: 'type', name: 'type',
message: 'What type of asset are you downloading?', message: 'What type of asset are you downloading?',
choices: ['movie/show', 'artist', 'album', 'book'] choices: ['movie/show', 'artist', 'album', 'book']
}]); });
if (type === 'artist') { if (type === 'artist') {
await downloadArtistImage(); await downloadArtistImage();

4
cli/package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "coryd", "name": "coryd",
"version": "1.0.0", "version": "1.1.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "coryd", "name": "coryd",
"version": "1.0.0", "version": "1.1.0",
"dependencies": { "dependencies": {
"chalk": "^5.4.1", "chalk": "^5.4.1",
"commander": "^14.0.0", "commander": "^14.0.0",

View file

@ -1,7 +1,7 @@
{ {
"name": "coryd", "name": "coryd",
"version": "1.0.0", "version": "1.1.0",
"description": "The CLI for my site that helps manage assets and other changes.", "description": "The CLI for my site to run scripts, manage and download assets.",
"type": "module", "type": "module",
"bin": { "bin": {
"coryd": "./bin/index.js" "coryd": "./bin/index.js"