lyricist
Version:
Fetches song lyrics using the Genius.com API and website.
205 lines (147 loc) • 5.67 kB
JavaScript
;
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
const fetch = require('node-fetch');
const cheerio = require('cheerio');
module.exports = class Lyricist {
constructor(accessToken) {
if (!accessToken) throw new Error('No access token provided to lyricist!');
this.accessToken = accessToken;
}
/*
Main request function
*/
_request(path) {
var _this = this;
return _asyncToGenerator(function* () {
const url = `https://api.genius.com/${path}`;
const headers = {
Authorization: `Bearer ${_this.accessToken}`
};
// Fetch result and parse it as JSON
const body = yield fetch(url, { headers });
const result = yield body.json();
// Handle errors
if (result.error) throw new Error(`${result.error}: ${result.error_description}`);
if (result.meta.status !== 200) throw new Error(`${result.meta.status}: ${result.meta.message}`);
return result.response;
})();
}
/*
Get song by ID
*/
song(id, { fetchLyrics = false, textFormat = 'dom' } = {}) {
var _this2 = this;
return _asyncToGenerator(function* () {
if (!id) throw new Error('No ID was provided to lyricist.song()');
const path = `songs/${id}?text_format=${textFormat}`;
var _ref = yield _this2._request(path);
const song = _ref.song;
const lyrics = fetchLyrics ? yield _this2._scrapeLyrics(song.url) : null;
return Object.assign({ lyrics }, song);
})();
}
/*
Get album by ID
*/
album(id, { fetchTracklist = false, textFormat = 'dom' } = {}) {
var _this3 = this;
return _asyncToGenerator(function* () {
if (!id) throw new Error('No ID was provided to lyricist.album()');
const path = `albums/${id}?text_format=${textFormat}`;
var _ref2 = yield _this3._request(path);
const album = _ref2.album;
const tracklist = fetchTracklist ? yield _this3._scrapeTracklist(album.url) : null;
return Object.assign({ tracklist }, album);
})();
}
/* Get artist by ID */
artist(id, { textFormat = 'dom' } = {}) {
var _this4 = this;
return _asyncToGenerator(function* () {
if (!id) throw new Error('No ID was provided to lyricist.artist()');
const path = `artists/${id}?text_format=${textFormat}`;
var _ref3 = yield _this4._request(path);
const artist = _ref3.artist;
return artist;
})();
}
/*
Get artist by exact name (undocumented, likely to change)
Potentially unreliable, use at own risk! ⚠️
*/
artistByName(name, opts) {
var _this5 = this;
return _asyncToGenerator(function* () {
const slug = _this5._geniusSlug(name);
const id = yield _this5._scrapeArtistPageForArtistID(slug);
return _this5.artist(id, opts);
})();
}
/* Get artist songs */
songsByArtist(id, { page = 1, perPage = 20, sort = 'title' } = {}) {
var _this6 = this;
return _asyncToGenerator(function* () {
if (!id) throw new Error('No ID was provided to lyricist.songsByArtist()');
const path = `artists/${id}/songs?per_page=${perPage}&page=${page}&sort=${sort}`;
var _ref4 = yield _this6._request(path);
const songs = _ref4.songs;
return songs;
})();
}
/* Search (for songs) */
search(query) {
var _this7 = this;
return _asyncToGenerator(function* () {
if (!query) throw new Error('No query was provided to lyricist.search()');
const path = `search?q=${query}`;
const response = yield _this7._request(path);
return response.hits.map(function (hit) {
return hit.result;
});
})();
}
/* Scrape tracklist */
_scrapeTracklist(url) {
return _asyncToGenerator(function* () {
const html = yield fetch(url).then(function (res) {
return res.text();
});
const $ = cheerio.load(html);
const json = $('meta[itemprop="page_data"]').attr('content');
const parsed = JSON.parse(json);
const songs = parsed.album_appearances;
return songs.map(function ({ song, track_number }) {
return Object.assign({ track_number }, song);
});
})();
}
/* Scrape song lyrics */
_scrapeLyrics(url) {
return _asyncToGenerator(function* () {
const response = yield fetch(url);
const text = yield response.text();
const $ = cheerio.load(text);
return $('.lyrics').text().trim();
})();
}
/* Get slug from name/title */
_geniusSlug(string) {
// Probably not 100% accurate yet
// Currently only used by undocumented artistByName function
const slug = string.trim().replace(/\s+/g, '-').replace("'", '').replace(/[^a-zA-Z0-9]/g, '-').toLowerCase();
// Uppercase first letter
return slug.charAt(0).toUpperCase() + slug.slice(1);
}
/* Scrape artist page to retrieve artist ID */
_scrapeArtistPageForArtistID(slug) {
return _asyncToGenerator(function* () {
const url = `https://genius.com/artists/${slug}`;
const html = yield fetch(url).then(function (res) {
return res.text();
});
const $ = cheerio.load(html);
const id = $('meta[name="newrelic-resource-path"]').attr('content').split('/').pop();
return id;
})();
}
};