182 lines
5.2 KiB
JavaScript
182 lines
5.2 KiB
JavaScript
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();
|
|
}
|
|
}
|