UNPKG

beatprints.js

Version:

A Node.js version of the original Python BeatPrints project (https://github.com/TrueMyst/BeatPrints/) by TrueMyst. Create eye-catching, Pinterest-style music posters effortlessly. BeatPrints integrates with Spotify and LRClib API to help you design custom

89 lines (88 loc) 3.51 kB
import { Size } from "./constants.js"; import * as write from './write.js'; export function zip(array, other) { return array.map((item, index) => [item, other[index]]); } export function pickRandom(arr) { if (arr.length === 0) return null; return arr[~~(Math.random() * arr.length)]; } /** * Adds a flat index to each track name in a nested list. * @param {string[][]} nlist A nested array of track names. * @returns {string[][]} The modified nested list with flat indexes added to track names. */ export function addIndexes(nlist) { let index = 1; for (let i = 0; i < nlist.length; i++) { for (let j = 0; j < nlist[i]?.length; j++) { nlist[i][j] = `${index}. ${nlist[i]?.[j]}`; index++; } } return nlist; } /** * Distributes tracks into columns while ensuring they fit within the maximum allowed width. * @param {string[]} tracks A list of track names to be organized into columns. * @param {boolean} index If true, adds index numbers to the tracks. Defaults to false. * @returns {OrganizeResult} A tuple containing the organized columns of tracks and their respective widths. */ export function organizeTracks(tracks, index = false) { const font = write.fontPaths('Light'); const getColumnWidth = (col, idxWidth) => { const size = Size.TRACKS; return Math.max(...col.map(item => write.textWidth(item, font, size))) + idxWidth; }; const idxWidth = index ? write.textWidth("00. ", font, Size.TRACKS) : 0; let cols = []; let colWidths = []; const tracksCopy = [...tracks]; while (true) { cols = []; for (let i = 0; i < tracksCopy.length; i += Size.MAX_ROWS) { cols.push(tracksCopy.slice(i, i + Size.MAX_ROWS)); } colWidths = cols.map(col => getColumnWidth(col, idxWidth)); const totalWidth = colWidths.reduce((a, b) => a + b, 0) + Size.SPACING * (cols.length - 1); if (totalWidth <= Size.MAX_WIDTH) break; const widestIndex = colWidths.indexOf(Math.max(...colWidths)); const widestCol = cols[widestIndex]; if (!widestCol) break; const longest = widestCol.reduce((a, b) => (a.length > b.length ? a : b)); const indexToRemove = tracksCopy.indexOf(longest); if (indexToRemove >= 0) tracksCopy.splice(indexToRemove, 1); } if (index) { cols = addIndexes(cols); } return [cols, colWidths]; } /** * Creates a safe filename based on the song and artist names. * @param {string} song The name of a song. * @param {string} artist The name of the artist. * @returns {string} A sanitized filename that is safe for file systems. */ export function filename(song, artist) { const fullText = `${song} by ${artist}`; // Replace illegal characters with underscores and sanitize the text. let safeText = fullText .replace(/[<>:"/\\|?*\x00-\x1F\x7F]/g, '_') .trim() .replace(/^\.+|\.+$/g, '') .toLowerCase() .replace(/ /g, '_'); // Remove consecutive underscores safeText = safeText.replace(/_{2,}/, '_'); // Limit filename length to 255 characters (filesystem limit) safeText = safeText.slice(0, 255); // Append 3 random hexadecimal digits to make the filename unique const randomHex = Array.from({ length: 3 }, () => Math.floor(Math.random() * 16).toString(16)).join(''); const filename = `${safeText}_${randomHex}.png`; return filename; }