import fs from 'fs-extra'; import path from 'path'; import https from 'https'; import inquirer from 'inquirer'; import { fileURLToPath } from 'url'; import { loadConfig } from './config.js'; import { sanitizeMediaString } from './sanitize.js'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const downloadImage = (url, dest) => new Promise((resolve, reject) => { const file = fs.createWriteStream(dest); https.get(url, response => { if (response.statusCode !== 200) return reject(new Error(`Failed to download. Status: ${response.statusCode}`)); response.pipe(file); file.on('finish', () => file.close(resolve)); }).on('error', reject); }); const isValidTMDBUrl = (val) => /^https:\/\/image\.tmdb\.org\/t\/p\//.test(val) || '❌ Must be a valid TMDB image url'; export const downloadWatchingImages = async () => { const config = await loadConfig(); const { mediaType } = await inquirer.prompt([{ type: 'list', name: 'mediaType', message: 'Movie or a show?', choices: Object.keys(config.mediaPaths) }]); const { tmdbId } = await inquirer.prompt([{ name: 'tmdbId', message: 'Enter the TMDB ID:' }]); const { posterUrl, backdropUrl } = await inquirer.prompt([{ name: 'posterUrl', message: 'Enter the poster url:', validate: isValidTMDBUrl }, { name: 'backdropUrl', message: 'Enter the backdrop url:', validate: isValidTMDBUrl }]); const types = [{ type: 'poster', url: posterUrl }, { type: 'backdrop', url: backdropUrl }]; for (const { type, url } of types) { const fileName = `${type}-${tmdbId}.jpg`; const targetSubPath = config.mediaPaths[mediaType][type]; if (!targetSubPath) { console.error(`❌ Missing path for ${mediaType}/${type}. Check your config.`); continue; } const targetDir = path.join(config.storageDir, targetSubPath); const finalPath = path.join(targetDir, fileName); await fs.ensureDir(targetDir); await downloadImage(url, finalPath); console.log(`✅ Saved ${type} to: ${finalPath}`); } } export const downloadArtistImage = async () => { const config = await loadConfig(); const { artistName, imageUrl } = await inquirer.prompt([{ name: 'artistName', message: 'Enter the artist name:' }, { name: 'imageUrl', message: 'Enter the artist image url:', validate: (val) => { try { new URL(val); return true; } catch { return '❌ Must be a valid url.'; } } }]); const sanitizedName = sanitizeMediaString(artistName); const fileName = `${sanitizedName}.jpg`; const targetDir = path.join(config.storageDir, 'artists'); const finalPath = path.join(targetDir, fileName); await fs.ensureDir(targetDir); await downloadImage(imageUrl, finalPath); console.log(`✅ Saved artist image to: ${finalPath}`); } export const downloadAlbumImage = async () => { const config = await loadConfig(); const { artistName, albumName, imageUrl } = await inquirer.prompt([{ name: 'artistName', message: 'Enter the artist name:' }, { name: 'albumName', message: 'Enter the album name:' }, { name: 'imageUrl', message: 'Enter the album image url:', validate: (val) => { try { new URL(val); return true; } catch { return '❌ Must be a valid url.'; } } }]); const artistSlug = sanitizeMediaString(artistName); const albumSlug = sanitizeMediaString(albumName); const fileName = `${artistSlug}-${albumSlug}.jpg`; const targetDir = path.join(config.storageDir, config.albumPath); const finalPath = path.join(targetDir, fileName); await fs.ensureDir(targetDir); await downloadImage(imageUrl, finalPath); console.log(`✅ Saved album image to: ${finalPath}`); } export const downloadBookImage = async () => { const config = await loadConfig(); const { isbn, bookTitle, imageUrl } = await inquirer.prompt([{ name: 'isbn', message: 'Enter the ISBN (no spaces):', validate: (val) => /^[a-zA-Z0-9-]+$/.test(val) || 'ISBN must contain only letters, numbers, or hyphens' }, { name: 'bookTitle', message: 'Enter the book title:' }, { name: 'imageUrl', message: 'Enter the book cover image URL:', validate: (val) => { try { new URL(val); return true; } catch { return 'Must be a valid URL'; } } }]); const titleSlug = sanitizeMediaString(bookTitle); const fileName = `${isbn}-${titleSlug}.jpg`; const targetDir = path.join(config.storageDir, config.bookPath); const finalPath = path.join(targetDir, fileName); await fs.ensureDir(targetDir); await downloadImage(imageUrl, finalPath); console.log(`✅ Saved book cover to: ${finalPath}`); } export const downloadAsset = async () => { const { type } = await inquirer.prompt([{ type: 'list', name: 'type', message: 'What type of asset are you downloading?', choices: ['movie/show', 'artist', 'album', 'book'] }]); if (type === 'artist') { await downloadArtistImage(); } else if (type === 'album') { await downloadAlbumImage(); } else if (type === 'book') { await downloadBookImage(); } else { await downloadWatchingImages(); } }