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
73 lines (72 loc) • 2.85 kB
JavaScript
import * as lrc from 'lrclib-api';
import { NoLyricsAvailable, InvalidFormatError, InvalidSelectionError, LineLimitExceededError } from './errors.js';
/**
* A class for interacting with the LRClib API to fetch and manage song lyrics.
*/
export class Lyrics {
constructor() {
this.api = new lrc.Client();
}
/**
* Determines if a track is instrumental.
* @param {TrackMetadata} metadata The metadata of the track.
* @returns {boolean} True if the track is instrumental (i.e., no lyrics found), false otherwise.
* @throws `NoLyricsAvailable` if no lyrics are found for the specified track and artist.
*/
async checkInstrumental(metadata) {
const result = await this.api.findLyrics({ track_name: metadata.name, artist_name: metadata.artist });
return result.instrumental;
}
async getLyrics(metadata, index = false) {
this.metadata = metadata;
const lyrics = (await this.api.findLyrics({
track_name: metadata.name,
artist_name: metadata.artist
})).plainLyrics;
if (await this.checkInstrumental(metadata))
return '';
if (!lyrics) {
throw new NoLyricsAvailable();
}
// Remove blanks between verses
const clearedLyrics = lyrics.split('\n').filter(x => x.trim());
return index ? clearedLyrics.map((verse, i) => `${i + 1}. ${verse}`).join('\n') : clearedLyrics.join('\n');
}
async selectLines(lyrics, selection) {
if (await this.checkInstrumental(this.metadata) || !lyrics)
return '';
const rawLines = lyrics.split('\n');
let lines;
// Remove indexes (e.g., 1. Verse) from lyrics if exists
if (rawLines.some(v => /^\d+\./.test(v))) {
lines = rawLines.map(line => line.replace(/^\d+\.\s*/, ''));
}
else
lines = rawLines;
const lineCount = lines.length;
try {
const pattern = /^\d+-\d+$/;
if (!pattern.test(selection)) {
throw new InvalidFormatError();
}
const selected = selection.split('-').map((num) => parseInt(num, 10));
if (selected.length !== 2 ||
selected[0] >= selected[1] ||
selected[0] <= 0 ||
selected[1] > lineCount) {
throw new InvalidSelectionError();
}
const extracted = lines.slice(selected[0] - 1, selected[1]);
const selectedLines = extracted.filter(line => line !== '');
if (selectedLines.length !== 4) {
throw new LineLimitExceededError();
}
return selectedLines.join('\n');
}
catch (err) {
if (err instanceof Error) {
throw err;
}
}
}
}