UNPKG

lyrics-structure

Version:

Parse lyrics with bracketed sections into structured parts, with slide generation for presentations

92 lines (91 loc) 3.31 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getLyricsParts = getLyricsParts; /** * Splits lyrics into structured parts, handling named sections, repetitions, and indications. * Works with lyrics formatted using square brackets for section names and optional parentheses for indications. * * Example input: * ``` * [verse 1] (first time) * Lyrics content here * [/verse 1] * * [chorus] * Chorus lyrics * [/chorus] * ``` * * @param lyrics - The input lyrics text to be parsed into parts * @returns Array of LyricPart objects containing structured lyrics data */ function getLyricsParts(lyrics) { const parts = []; const seenParts = new Map(); const partContentMap = new Map(); // First pass: extract all content with closing tags const cleanedText = lyrics.replace(/\[([^\]]+)\](?:\s*\(([^)]+)\))?\s*([\s\S]*?)\[\/\1\]/g, (match, key, indication, content) => { if (!partContentMap.has(key)) { partContentMap.set(key, content.trim()); } return `[${key}]${indication ? ` (${indication})` : ''}`; }); // Split the lyrics into lines and process them const lines = cleanedText.split('\n'); let currentPart = null; let currentUnnamedContent = []; for (let i = 0; i < lines.length; i++) { const line = lines[i].trim(); // Check for part start const partStartMatch = line.match(/^\[([^\]]+)\](?:\s*\(([^)]+)\))?$/); if (partStartMatch) { // If we have accumulated unnamed content, add it as a part if (currentUnnamedContent.length > 0) { parts.push({ name: undefined, repetition: false, indication: null, content: currentUnnamedContent.join('\n'), }); currentUnnamedContent = []; } const partName = partStartMatch[1]; const indication = partStartMatch[2] || null; // Check if this part has been seen before const partCount = (seenParts.get(partName) || 0) + 1; seenParts.set(partName, partCount); currentPart = { name: partName, repetition: partCount > 1, indication, content: partContentMap.get(partName), }; parts.push(currentPart); continue; } // Handle content without a part container if (line && !line.startsWith('[') && !line.startsWith('[/')) { currentUnnamedContent.push(line); } else if (currentUnnamedContent.length > 0) { // If we hit a part marker or empty line, add the accumulated content parts.push({ name: undefined, repetition: false, indication: null, content: currentUnnamedContent.join('\n'), }); currentUnnamedContent = []; } } // Add any remaining unnamed content if (currentUnnamedContent.length > 0) { parts.push({ name: undefined, repetition: false, indication: null, content: currentUnnamedContent.join('\n'), }); } return parts; }