lyric-karaoke-cli
Version:
A CLI application for displaying song lyrics in a karaoke-style format
240 lines (213 loc) • 6.09 kB
JavaScript
/**
* Caching module for storing fetched song data
*/
const fs = require('fs').promises;
const path = require('path');
const { logger } = require('../utils');
// Cache directory path
const CACHE_DIR = path.join(process.cwd(), '.cache');
const CACHE_FILE = path.join(CACHE_DIR, 'songs.json');
const CACHE_EXPIRY = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
// Cache object to store data in memory
let cache = {};
/**
* Initialize cache
*/
async function init() {
try {
// Create cache directory if it doesn't exist
await fs.mkdir(CACHE_DIR, { recursive: true });
// Load cache from file if it exists
try {
const data = await fs.readFile(CACHE_FILE, 'utf8');
cache = JSON.parse(data);
// Clean expired cache entries
const now = Date.now();
Object.keys(cache).forEach(key => {
if (now - cache[key].timestamp > CACHE_EXPIRY) {
delete cache[key];
}
});
await saveCache();
logger.info('Cache loaded successfully');
} catch (error) {
if (error.code !== 'ENOENT') {
logger.error('Error loading cache:', error.message);
}
// Initialize empty cache if file doesn't exist or can't be read
cache = {};
await saveCache();
logger.info('Initialized new cache');
}
} catch (error) {
logger.error('Failed to initialize cache:', error.message);
throw error;
}
}
/**
* Save cache to file
*/
async function saveCache() {
try {
await fs.writeFile(CACHE_FILE, JSON.stringify(cache, null, 2), 'utf8');
} catch (error) {
logger.error('Error saving cache:', error.message);
throw error;
}
}
/**
* Get cached data by key
* @param {string} key - Cache key
* @returns {Promise<any>} - Cached data or null if not found/expired
*/
async function get(key) {
try {
const cacheEntry = cache[key];
if (!cacheEntry) {
return null;
}
const now = Date.now();
if (now - cacheEntry.timestamp > CACHE_EXPIRY) {
// Entry has expired
delete cache[key];
await saveCache();
return null;
}
return cacheEntry.data;
} catch (error) {
logger.error('Error retrieving from cache:', error.message);
return null;
}
}
/**
* Set data in cache
* @param {string} key - Cache key
* @param {any} data - Data to cache
* @returns {Promise<void>}
*/
async function set(key, data) {
try {
cache[key] = {
data,
timestamp: Date.now()
};
await saveCache();
} catch (error) {
logger.error('Error setting cache:', error.message);
throw error;
}
}
/**
* Clear the cache
* @returns {Promise<void>}
*/
async function clear() {
try {
cache = {};
await saveCache();
logger.info('Cache cleared successfully');
} catch (error) {
logger.error('Error clearing cache:', error.message);
throw error;
}
}
/**
* Get list of recent songs from cache
* @returns {Promise<Array>} - List of recent songs
*/
async function getRecentSongs() {
try {
const songEntries = Object.entries(cache)
.filter(([key, _]) => key.startsWith('song:'))
.map(([_, entry]) => ({
...entry.data,
cachedAt: entry.timestamp
}))
.sort((a, b) => b.cachedAt - a.cachedAt)
.slice(0, 10); // Get most recent 10 songs
return songEntries;
} catch (error) {
logger.error('Error getting recent songs:', error.message);
return [];
}
}
/**
* Store lyrics data in the cache
* @param {string} artistName - Name of the artist
* @param {string} songTitle - Title of the song
* @param {string} lyrics - Full lyrics text
* @param {Object} metadata - Additional song metadata
* @returns {Promise<void>}
*/
async function store(artistName, songTitle, lyrics, metadata = {}) {
try {
const key = `lyrics:${artistName.toLowerCase()}:${songTitle.toLowerCase()}`;
const data = {
artistName,
songTitle,
lyrics,
metadata,
retrievedAt: new Date().toISOString()
};
await set(key, data);
logger.info(`Cached lyrics for "${songTitle}" by ${artistName}`);
return true;
} catch (error) {
logger.error('Error storing lyrics in cache:', error.message);
return false;
}
}
/**
* Retrieve lyrics data from the cache
* @param {string} artistName - Name of the artist
* @param {string} songTitle - Title of the song
* @returns {Promise<Object|null>} - Lyrics data or null if not found
*/
async function retrieve(artistName, songTitle) {
try {
const key = `lyrics:${artistName.toLowerCase()}:${songTitle.toLowerCase()}`;
const data = await get(key);
if (!data) {
logger.info(`No cached lyrics found for "${songTitle}" by ${artistName}`);
return null;
}
logger.info(`Retrieved cached lyrics for "${songTitle}" by ${artistName}`);
return data;
} catch (error) {
logger.error('Error retrieving lyrics from cache:', error.message);
return null;
}
}
/**
* Get list of recent lyrics from cache
* @param {number} limit - Maximum number of entries to return
* @returns {Promise<Array>} - List of recent lyrics entries
*/
async function getRecentLyrics(limit = 10) {
try {
const lyricsEntries = Object.entries(cache)
.filter(([key, _]) => key.startsWith('lyrics:'))
.map(([_, entry]) => ({
artistName: entry.data.artistName,
songTitle: entry.data.songTitle,
retrievedAt: entry.data.retrievedAt,
cachedAt: entry.timestamp
}))
.sort((a, b) => b.cachedAt - a.cachedAt)
.slice(0, limit);
return lyricsEntries;
} catch (error) {
logger.error('Error getting recent lyrics:', error.message);
return [];
}
}
module.exports = {
init,
get,
set,
clear,
getRecentSongs,
store,
retrieve,
getRecentLyrics
};