audible-api
Version:
A Node.js API for searching the audible website
238 lines (196 loc) • 9.61 kB
JavaScript
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
import { load as loadPage } from "cheerio";
import fetch from "node-fetch";
import { URL, URLSearchParams } from "url";
import { abridged, audibleOriginal, audiobookFormat, pageSizes, plusCatalog, releaseTimes, searchCategories, searchDurations, searchLanguages, siteCountries, unabridged, whispersync } from "./data/audible-search-fields";
import getAudibleBook from "./get-audible-book";
import { parseLanguage } from "./utils/language";
import { cleanNarratorUrl, cleanTitle, cleanUrl } from "./utils/string";
import { getDurationFromStr } from "./utils/time";
/**
* A set of parameters used to search the Audible website
*/
/**
* Search the Audible website for a list of books matching your search criteria
*
* @param options - An object containing your search criteria
* @returns A promise returning an array of book objects from the search results, along with total results
*/
export default function searchAudible() {
return _searchAudible.apply(this, arguments);
}
function _searchAudible() {
_searchAudible = _asyncToGenerator(function* () {
var {
keywords,
title,
author,
narrator,
publisher,
category,
isAudibleOriginal = false,
isPlusCatalog = false,
releaseTime,
durations = [],
languages = [],
isWhisperSync = false,
isAbridged,
pageNum = 1,
pageSize = 20,
getFull = false,
site = "us"
} = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var {
url: baseUrl
} = siteCountries[site] || siteCountries.us;
var baseSearchUrl = "".concat(baseUrl, "/search");
var realPageSize = pageSizes.includes(pageSize) ? pageSize : pageSizes[0];
var paramsObj = _objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread({}, title && {
title
}), keywords && {
keywords
}), author && {
searchAuthor: author
}), narrator && {
narrator
}), publisher && {
publisher
}), isAbridged === true && abridged), isAbridged === false && unabridged), isAudibleOriginal && audibleOriginal), isPlusCatalog && plusCatalog), isWhisperSync && whispersync), {}, {
page: pageNum.toString(),
pageSize: realPageSize.toString(),
ipRedirectOverride: "true"
}, audiobookFormat);
var params = new URLSearchParams(paramsObj);
if (releaseTime && releaseTimes[releaseTime]) {
params.append("publication_date", releaseTimes[releaseTime]);
}
if (category && searchCategories[category]) {
params.append("node", searchCategories[category]);
}
durations.forEach(duration => {
if (searchDurations[duration]) {
params.append("feature_seven_browse-bin", searchDurations[duration]);
}
});
languages.forEach(lang => {
var langCode = searchLanguages[lang.toLowerCase()];
if (langCode) {
params.append("feature_six_browse-bin", langCode);
}
});
var searchUrl = "".concat(baseSearchUrl, "?").concat(params.toString());
var res = yield fetch(searchUrl);
var body = yield res.text();
var page = loadPage(body);
var audibleItems = [];
var resultsStr = page(".resultsSummarySubheading").eq(0).text().trim();
var totalResultsMatch = resultsStr.match(/([0-9,]+) results$/);
var totalResults = totalResultsMatch ? Number(totalResultsMatch[1]) : 0;
page(".productListItem").each((i, elSel) => {
var _newBook$url;
var newBook = {
title: page("h3", elSel).text().trim(),
coverUrl: page("img", elSel).attr("data-lazy"),
authors: [],
narrators: []
}; // Get book's language
var newLanguage = parseLanguage(page(".languageLabel", elSel).text().replace("Language:", "").trim());
if (newLanguage) {
newBook.language = newLanguage;
} // Get book's url
var urlStr = page("h3 a", elSel).attr("href");
if (urlStr) {
newBook.url = cleanUrl(new URL(urlStr, baseUrl).href);
} // Get book's clean title
if (newBook.title) {
newBook.cleanTitle = cleanTitle(newBook.title);
} // Get the book's duration
var runtimeStr = page(".runtimeLabel", elSel).text().trim();
if (runtimeStr) {
newBook.duration = getDurationFromStr(runtimeStr);
} // Get the book's ASIN
var asinMatch = (_newBook$url = newBook.url) === null || _newBook$url === void 0 ? void 0 : _newBook$url.match(/\/([A-Z0-9]+)$/);
if (asinMatch) {
newBook.asin = asinMatch[1];
} // Get the aggregate rating
var ratingValueStr = page(".ratingsLabel .bc-pub-offscreen", elSel).text().trim();
var ratingsValueMatch = ratingValueStr.match(/[0-9.]+/);
var ratingValue = Number((ratingsValueMatch === null || ratingsValueMatch === void 0 ? void 0 : ratingsValueMatch[0]) || 0);
var ratingCountStr = page(".ratingsLabel .bc-color-secondary", elSel).text().trim();
var ratingCountMatch = ratingCountStr.match(/[0-9,]+/);
var ratingCount = Number((ratingCountMatch === null || ratingCountMatch === void 0 ? void 0 : ratingCountMatch[0].replace(/,/g, "")) || 0);
newBook.rating = {
value: ratingValue,
count: ratingCount
};
page(".authorLabel a", elSel).each((ii, authorSel) => {
var authorEl = page(authorSel);
var newAuthor = {
name: authorEl.text().trim()
};
var authorUrlPath = authorEl.attr("href");
if (authorUrlPath) {
newAuthor.url = cleanUrl(new URL(authorUrlPath, baseUrl).href);
}
newBook.authors.push(newAuthor);
});
page(".authorLabel a", elSel).each((ii, narratorSel) => {
var narratorEl = page(narratorSel);
var newNarrator = {
name: narratorEl.text().trim()
};
var narratorUrlPath = narratorEl.attr("href");
if (narratorUrlPath) {
newNarrator.url = cleanNarratorUrl(new URL(narratorUrlPath, baseUrl).href);
}
newBook.narrators.push(newNarrator);
}); // Get series and series part
try {
var series = [];
page(".seriesLabel a", elSel).each((iii, seriesSel) => {
var el = page(seriesSel);
var newSeries = {
name: el.text().trim()
};
var seriesUrlPath = el.attr("href");
if (seriesUrlPath) {
newSeries.url = cleanUrl(new URL(seriesUrlPath, baseUrl).href);
}
series.push(newSeries);
});
var seriesArr = page(".seriesLabel", elSel).text().replace(/\n/g, "").trim() // remove the beginning text from the series
.replace("Series: ", "").split(", ").map(item => item.trim());
seriesArr.forEach((seriesStr, seriesI) => {
if (seriesStr.includes("Book")) {
var seriesPart = Number(seriesStr.replace("Book ", ""));
if (seriesPart) {
var matchingBookIndex = series.findIndex(item => item.name === seriesArr[seriesI - 1]);
if (matchingBookIndex > -1) {
series[matchingBookIndex].part = seriesPart;
}
}
}
});
newBook.series = series;
} catch (err) {// console.warn(`ERROR PARSING AUDIBLE SERIES'\n${err.stack}`);
}
audibleItems.push(newBook);
});
if (getFull) {
audibleItems = yield Promise.all(audibleItems.map(book => book.asin ? getAudibleBook(book.asin, {
site
}) : book));
} // console.log(JSON.stringify(audibleItems, null, 2));
var searchResults = {
totalResults,
results: audibleItems
};
return searchResults;
});
return _searchAudible.apply(this, arguments);
}
//# sourceMappingURL=search-audible.js.map