Skip to main content

Overview

The CacheManager class provides a lightweight caching system for La Urban’s radio data with intelligent change detection. It tracks song changes and live state transitions to minimize unnecessary UI updates.

Features

  • 15-second update interval for main data
  • Song change detection by ID comparison
  • Live state transition tracking
  • Minimal memory footprint

Constructor

const cacheManager = new CacheManager();
Creates a new CacheManager instance with default settings.

Properties

laurbanData
Object|null
default:"null"
Cached radio data from La Urban API
lastCheck
Number
default:"0"
Timestamp (in milliseconds) of the last cache update
currentSongId
String|Number|null
default:"null"
ID of the currently cached song
currentLiveState
Boolean
default:"false"
Current live streaming state
updateInterval
Number
default:"15000"
Update interval in milliseconds (15 seconds)

Methods

needsUpdate()

Checks if the cache needs to be refreshed based on the update interval.
const shouldUpdate = cacheManager.needsUpdate();
Returns: Boolean - true if cache is stale or empty, false otherwise Example:
if (cacheManager.needsUpdate()) {
  const freshData = await fetchRadioData();
  cacheManager.update(freshData);
}
Behavior:
  • Returns true if no data is cached (laurbanData === null)
  • Returns true if time since last check >= updateInterval (15 seconds)
  • Returns false if cache is still fresh

hasChanged()

Compares new data with cached data to detect significant changes.
const dataChanged = cacheManager.hasChanged(newData);
newData
Object
required
New radio data object to compare against cached data
Returns: Boolean - true if song changed or live state changed, false otherwise Example:
const newData = await fetchRadioData();

if (cacheManager.hasChanged(newData)) {
  console.log('Song or live state has changed!');
  updateUI(newData);
}

cacheManager.update(newData);
Detection Logic:
Compares now_playing.id between cached and new data:
this.laurbanData.now_playing?.id !== newData.now_playing?.id
Logs: 🎵 Detectado cambio de canción
Compares live.is_live between cached and new data:
this.laurbanData.live?.is_live !== newData.live?.is_live
Logs: 🎥 Detectado cambio en estado de live
Returns true if:
  • No data is currently cached
  • New data is null/undefined
  • Song ID has changed
  • Live streaming state has changed

update()

Updates the cache with new data and refreshes tracking properties.
cacheManager.update(newData);
newData
Object
required
Radio data object to store in cache
Example:
const radioData = {
  now_playing: {
    id: 12345,
    song: { title: "Song Title", artist: "Artist Name" }
  },
  live: {
    is_live: false
  }
};

cacheManager.update(radioData);
Behavior:
  • Stores the entire data object in laurbanData
  • Updates lastCheck to current timestamp
  • Caches currentSongId from newData.now_playing?.id
  • Caches currentLiveState from newData.live?.is_live (defaults to false)

get()

Retrieves the currently cached data.
const cachedData = cacheManager.get();
Returns: Object|null - The cached radio data or null if no data is cached Example:
const data = cacheManager.get();

if (data) {
  console.log('Current song:', data.now_playing.song.title);
  console.log('Is live:', data.live.is_live);
}

Usage Patterns

const cache = new CacheManager();

async function updateRadioData() {
  if (cache.needsUpdate()) {
    const newData = await fetch('/api/nowplaying').then(r => r.json());
    
    if (cache.hasChanged(newData)) {
      // Only update UI when data actually changed
      updatePlayerUI(newData);
    }
    
    cache.update(newData);
  }
}

// Poll every 5 seconds, but only fetch if cache is stale (15s)
setInterval(updateRadioData, 5000);

Expected Data Structure

The CacheManager expects radio data with the following structure:
{
  now_playing: {
    id: Number | String,        // Unique song identifier
    song: {
      title: String,
      artist: String,
      // ... other song metadata
    },
    elapsed: Number,            // Seconds elapsed
    duration: Number,           // Total duration
    // ... other now_playing fields
  },
  live: {
    is_live: Boolean,          // Live streaming state
    streamer_name: String,     // Streamer name if live
    // ... other live fields
  }
  // ... other API fields
}

Logger Integration

The CacheManager includes a fallback logger implementation if the global logger is not available:
const logger = globalThis.logger || {
  dev: console.log.bind(console),
  info: console.log.bind(console),
  success: console.log.bind(console),
  warn: console.warn.bind(console),
  error: console.error.bind(console),
  critical: console.error.bind(console)
};
This ensures logging always works even if loaded before the main Logger module.

Performance Considerations

The 15-second update interval balances freshness with API load:
  • Too frequent: Wastes bandwidth, increases server load
  • Too slow: Users see stale information
  • 15 seconds: Optimal for song change detection while being API-friendly
You can poll more frequently (e.g., every 5 seconds) and rely on needsUpdate() to prevent unnecessary fetches.
The hasChanged() method prevents unnecessary DOM updates:
  • Only update UI when song actually changes
  • Avoid re-rendering heavy components
  • Reduce visual flicker and animations
  • Improve battery life on mobile devices

Export

The class is exported to the global scope:
globalThis.CacheManager = CacheManager;
Access it anywhere in your application:
const cache = new CacheManager();

Build docs developers (and LLMs) love