2 music mood
gerrit edited this page 2026-06-24 10:24:27 +00:00
const axios = require('axios');

// --- CONFIGURATION ---
const LIDARR_URL = 'http://localhost:8686';
const LIDARR_API_KEY = 'YOUR_LIDARR_API_KEY';
const OLLAMA_URL = 'http://localhost:11434';
const OLLAMA_MODEL = 'llama3'; // or mistral, qwen2.5, phi3, etc.

/**
 * 1. Fetch comprehensive Artist data from Lidarr
 */
async function getLidarrArtist(artistId) {
    try {
        const response = await axios.get(`${LIDARR_URL}/api/v1/artist/${artistId}`, {
            params: { apikey: LIDARR_API_KEY }
        });
        
        const artist = response.data;
        return {
            name: artist.artistName,
            genres: artist.genres || [],
            overview: artist.overview || "No biography available."
        };
    } catch (error) {
        console.error(`❌ Error fetching Lidarr artist (${artistId}):`, error.message);
        throw error;
    }
}

/**
 * 2. Send purely local database context to Ollama
 */
async function generateMoodWithOllama(context) {
    const prompt = `
You are an expert musicologist. Analyze the local media library metadata provided below to generate a highly nuanced emotional profile for this album.

--- LIBRARY METADATA ---
Artist Name: ${context.artistName}
Album Title: ${context.albumTitle}
Artist Genres/Styles: ${context.genres.join(', ')}
Artist Biography & Context: ${context.biography}
-----------------------

Respond ONLY with a valid JSON object matching the schema below. Do not include introductory text, conversational remarks, or markdown formatting blocks.

Schema:
{
  "dominantMood": "The single most prevailing emotion of the album",
  "subtextMoods": ["Up to three supporting emotional textures"],
  "situationalContext": "The ideal setting, activity, or scenario to listen to this music",
  "emotionalVibe": "A one-sentence breakdown of the sonic and emotional journey"
}
`;

    try {
        const response = await axios.post(`${OLLAMA_URL}/api/generate`, {
            model: OLLAMA_MODEL,
            prompt: prompt,
            stream: false,
            format: 'json' // Enforces JSON constraints natively inside Ollama
        });

        return JSON.parse(response.data.response);
    } catch (error) {
        console.error('❌ Error communicating with Ollama:', error.message);
        throw error;
    }
}

/**
 * 3. Pipeline Orchestrator
 */
async function runMoodPipeline(lidarrArtistId, albumTitle) {
    console.log(`🚀 Processing Album: "${albumTitle}" (Lidarr Artist ID: ${lidarrArtistId})...`);
    
    try {
        // Step 1: Pull data from Lidarr
        const artist = await getLidarrArtist(lidarrArtistId);
        console.log(`🎵 Found local artist entry: ${artist.name}`);
        
        // Step 2: Bundle context
        const contextBundle = {
            artistName: artist.name,
            albumTitle: albumTitle,
            genres: artist.genres,
            biography: artist.overview
        };

        // Step 3: Prompt local LLM
        console.log(`🤖 Analyzing data via local Ollama instance (${OLLAMA_MODEL})...`);
        const moodProfile = await generateMoodWithOllama(contextBundle);
        
        console.log('\n✅ Successfully Generated Mood Profile:');
        console.log(JSON.stringify(moodProfile, null, 2));
        
        return moodProfile;

    } catch (error) {
        console.error('❌ Pipeline execution failed.');
    }
}

// --- EXECUTION EXAMPLE ---
// Replace '1' with an actual Artist ID from your Lidarr library
runMoodPipeline(1, 'Kid A');