@library-pals/isbn 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,6 +7,7 @@ following providers:
7
7
  - [Open Library Books API](https://openlibrary.org/dev/docs/api/books)
8
8
  - [ISBNdb API](https://isbndb.com/apidocs/v2) using API key in the environment
9
9
  variable `ISBNDB_API_KEY`
10
+ - [Libro.fm](https://libro.fm/explore)
10
11
 
11
12
  ## Acknowledgements
12
13
 
@@ -65,7 +66,7 @@ information.
65
66
  "authors": ["Jeff VanderMeer"],
66
67
  "description": "Describes the 12th expedition to “Area X,” a region cut off from the continent for decades, by a group of intrepid women scientists who try to ignore the high mortality rates of those on the previous 11 missions. Original. 75,000 first printing.",
67
68
  "pageCount": 209,
68
- "printType": "BOOK",
69
+ "format": "book",
69
70
  "categories": [
70
71
  "Fiction",
71
72
  "Fiction / General",
@@ -97,7 +98,7 @@ information.
97
98
  "authors": ["Jeff VanderMeer"],
98
99
  "description": "Area X has been cut off from the rest of the continent for decades. Nature has reclaimed the last vestiges of human civilization. The twelfth expedition arrives expecting the unexpected, and Area X delivers. They discover a massive topographic anomaly and life-forms that surpass understanding. But it's the surprises that came across the border with them, and the secrets the expedition members are keeping from one another that change everything.",
99
100
  "pageCount": 208,
100
- "printType": "BOOK",
101
+ "format": "book",
101
102
  "categories": [
102
103
  "Nebula Award Winner",
103
104
  "award:nebula_award=novel",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@library-pals/isbn",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Find books by ISBN",
5
5
  "exports": "./src/index.js",
6
6
  "types": "./src/index.d.ts",
@@ -26,7 +26,8 @@
26
26
  "google",
27
27
  "openlibrary",
28
28
  "api",
29
- "isbndb"
29
+ "isbndb",
30
+ "librofm"
30
31
  ],
31
32
  "author": "Katy DeCorah <@katydecorah>",
32
33
  "license": "AGPL-3.0-or-later",
package/src/index.d.ts CHANGED
@@ -4,8 +4,8 @@
4
4
  * @property {string} title - The title of the book.
5
5
  * @property {string[]} authors - The authors of the book.
6
6
  * @property {string} description - The overview of the book.
7
- * @property {number} pageCount - The number of pages in the book.
8
- * @property {string} printType - The print type of the book.
7
+ * @property {number} [pageCount] - The number of pages in the book.
8
+ * @property {string} format - The format of the book.
9
9
  * @property {string[]} categories - The subjects or categories of the book.
10
10
  * @property {string} publisher - The publisher of the book.
11
11
  * @property {string} publishedDate - The date the book was published.
@@ -27,6 +27,7 @@ export default class Isbn {
27
27
  GOOGLE: string;
28
28
  OPENLIBRARY: string;
29
29
  ISBNDB: string;
30
+ LIBROFM: string;
30
31
  };
31
32
  /**
32
33
  * Sets the providers for the ISBN lookup.
@@ -65,11 +66,11 @@ export type Book = {
65
66
  /**
66
67
  * - The number of pages in the book.
67
68
  */
68
- pageCount: number;
69
+ pageCount?: number;
69
70
  /**
70
- * - The print type of the book.
71
+ * - The format of the book.
71
72
  */
72
- printType: string;
73
+ format: string;
73
74
  /**
74
75
  * - The subjects or categories of the book.
75
76
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;GAeG;AAEH;;;GAGG;AAEH;IACE;;OAEG;IACH,wDAA+B;IAG7B;;;;MAAoC;IAGtC;;;;;;OAMG;IACH,oBALW,MAAM,EAAE,GACN,MAAM,CAwBlB;IAED;;;;;;OAMG;IACH,cALW,MAAM,YACN,kBAAkB,GAChB,QAAQ,IAAI,CAAC,CAkBzB;CACF;;;;;UAlFa,MAAM;;;;WACN,MAAM;;;;aACN,MAAM,EAAE;;;;iBACR,MAAM;;;;eACN,MAAM;;;;eACN,MAAM;;;;gBACN,MAAM,EAAE;;;;eACR,MAAM;;;;mBACN,MAAM;;;;eACN,MAAM,GAAG,SAAS;;;;gBAClB,MAAM,GAAG,SAAS;;;;WAClB,MAAM;;;;kBACN,MAAM;;wBAIP,OAAO,yBAAyB,EAAE,SAAS;iCAC3C,OAAO,OAAO,EAAE,kBAAkB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;GAeG;AAEH;;;GAGG;AAEH;IACE;;OAEG;IACH,wDAA+B;IAG7B;;;;;MAAoC;IAGtC;;;;;;OAMG;IACH,oBALW,MAAM,EAAE,GACN,MAAM,CAwBlB;IAED;;;;;;OAMG;IACH,cALW,MAAM,YACN,kBAAkB,GAChB,QAAQ,IAAI,CAAC,CAkBzB;CACF;;;;;UAlFa,MAAM;;;;WACN,MAAM;;;;aACN,MAAM,EAAE;;;;iBACR,MAAM;;;;gBACN,MAAM;;;;YACN,MAAM;;;;gBACN,MAAM,EAAE;;;;eACR,MAAM;;;;mBACN,MAAM;;;;eACN,MAAM,GAAG,SAAS;;;;gBAClB,MAAM,GAAG,SAAS;;;;WAClB,MAAM;;;;kBACN,MAAM;;wBAIP,OAAO,yBAAyB,EAAE,SAAS;iCAC3C,OAAO,OAAO,EAAE,kBAAkB"}
package/src/index.js CHANGED
@@ -10,8 +10,8 @@ import {
10
10
  * @property {string} title - The title of the book.
11
11
  * @property {string[]} authors - The authors of the book.
12
12
  * @property {string} description - The overview of the book.
13
- * @property {number} pageCount - The number of pages in the book.
14
- * @property {string} printType - The print type of the book.
13
+ * @property {number} [pageCount] - The number of pages in the book.
14
+ * @property {string} format - The format of the book.
15
15
  * @property {string[]} categories - The subjects or categories of the book.
16
16
  * @property {string} publisher - The publisher of the book.
17
17
  * @property {string} publishedDate - The date the book was published.
@@ -14,10 +14,13 @@ export const OPENLIBRARY_API_BASE: "https://openlibrary.org";
14
14
  export const OPENLIBRARY_API_BOOK: "/isbn";
15
15
  export const ISBNDB_API_BASE: "https://api2.isbndb.com";
16
16
  export const ISBNDB_API_BOOK: "/book";
17
+ export const LIBROFM_API_BASE: "https://libro.fm";
18
+ export const LIBROFM_API_BOOK: "/audiobooks";
17
19
  export namespace PROVIDER_NAMES {
18
20
  let GOOGLE: string;
19
21
  let OPENLIBRARY: string;
20
22
  let ISBNDB: string;
23
+ let LIBROFM: string;
21
24
  }
22
25
  /**
23
26
  * Default providers for resolving ISBN information.
@@ -1 +1 @@
1
- {"version":3,"file":"provider-resolvers.d.ts","sourceRoot":"","sources":["provider-resolvers.js"],"names":[],"mappings":"AAIA;;;GAGG;AAEH;;;;GAIG;AACH,6BAHU,kBAAkB,CAK1B;AAEF,iEAAkE;AAClE,wDAAyD;AAEzD,6DAA8D;AAC9D,2CAA4C;AAE5C,wDAAyD;AACzD,sCAAuC;;;;;;AAOvC;;;GAGG;AACH,0CAIE;AACF;;EAIE;wBAxCW,MAAM,EAAE;iCACR,OAAO,OAAO,EAAE,kBAAkB;8BANjB,uBAAuB"}
1
+ {"version":3,"file":"provider-resolvers.d.ts","sourceRoot":"","sources":["provider-resolvers.js"],"names":[],"mappings":"AAKA;;;GAGG;AAEH;;;;GAIG;AACH,6BAHU,kBAAkB,CAK1B;AAEF,iEAAkE;AAClE,wDAAyD;AAEzD,6DAA8D;AAC9D,2CAA4C;AAE5C,wDAAyD;AACzD,sCAAuC;AAEvC,kDAAmD;AACnD,6CAA8C;;;;;;;AAS9C;;;GAGG;AACH,0CAKE;AAEF;;EAKE;wBAhDW,MAAM,EAAE;iCACR,OAAO,OAAO,EAAE,kBAAkB;8BAPjB,uBAAuB"}
@@ -1,6 +1,7 @@
1
1
  import { resolveGoogle } from "./providers/google.js";
2
2
  import { resolveOpenLibrary } from "./providers/open-library.js";
3
3
  import { resolveIsbnDb } from "./providers/isbndb.js";
4
+ import { resolveLibroFm } from "./providers/librofm.js";
4
5
 
5
6
  /**
6
7
  * @typedef {string[]} Providers
@@ -24,10 +25,15 @@ export const OPENLIBRARY_API_BOOK = "/isbn";
24
25
 
25
26
  export const ISBNDB_API_BASE = "https://api2.isbndb.com";
26
27
  export const ISBNDB_API_BOOK = "/book";
28
+
29
+ export const LIBROFM_API_BASE = "https://libro.fm";
30
+ export const LIBROFM_API_BOOK = "/audiobooks";
31
+
27
32
  export const PROVIDER_NAMES = {
28
33
  GOOGLE: "google",
29
34
  OPENLIBRARY: "openlibrary",
30
35
  ISBNDB: "isbndb",
36
+ LIBROFM: "librofm",
31
37
  };
32
38
 
33
39
  /**
@@ -38,9 +44,12 @@ export const DEFAULT_PROVIDERS = [
38
44
  PROVIDER_NAMES.GOOGLE,
39
45
  PROVIDER_NAMES.OPENLIBRARY,
40
46
  PROVIDER_NAMES.ISBNDB,
47
+ PROVIDER_NAMES.LIBROFM,
41
48
  ];
49
+
42
50
  export const PROVIDER_RESOLVERS = {
43
51
  [PROVIDER_NAMES.GOOGLE]: resolveGoogle,
44
52
  [PROVIDER_NAMES.OPENLIBRARY]: resolveOpenLibrary,
45
53
  [PROVIDER_NAMES.ISBNDB]: resolveIsbnDb,
54
+ [PROVIDER_NAMES.LIBROFM]: resolveLibroFm,
46
55
  };
@@ -99,7 +99,7 @@ export async function standardize(book, id, isbn) {
99
99
  authors: book.authors,
100
100
  description: book.description,
101
101
  pageCount: book.pageCount,
102
- printType: book.printType,
102
+ format: book.printType?.toLowerCase(),
103
103
  categories: formatCategories(categories),
104
104
  thumbnail: getLargestThumbnail(imageLinks),
105
105
  link: book.canonicalVolumeLink,
@@ -120,7 +120,7 @@ function standardize(book, isbn) {
120
120
  authors: book.authors,
121
121
  description: book.overview,
122
122
  pageCount: book.pages,
123
- printType: "BOOK",
123
+ format: "book",
124
124
  categories: book.subjects,
125
125
  thumbnail: book.image,
126
126
  publisher: book.publisher,
@@ -0,0 +1,123 @@
1
+ /**
2
+ * @typedef {import('../index.js').Book} Book
3
+ * @typedef {import('axios').AxiosRequestConfig} AxiosRequestConfig
4
+ */
5
+ /**
6
+ * Resolves book information from Libro.fm using the provided ISBN.
7
+ * @param {string} isbn - The ISBN of the book.
8
+ * @returns {Promise<Book>} The book information retrieved from the API.
9
+ * @throws {Error} If the API response code is not 200, or if no books are found with the provided ISBN, or if no volume information is found for the book.
10
+ */
11
+ export function resolveLibroFm(isbn: string): Promise<Book>;
12
+ /**
13
+ * Standardizes a book object by extracting relevant information from the provided book object.
14
+ * @param {string} data - The data to be standardized.
15
+ * @param {string} isbn - The book's ISBN.
16
+ * @param {string} url - The URL of the book.
17
+ * @returns {Promise<Book>} The standardized book object.
18
+ */
19
+ export function standardize(data: string, isbn: string, url: string): Promise<Book>;
20
+ /**
21
+ * @typedef {object} Person
22
+ * @property {string} name - The name of the person.
23
+ */
24
+ /**
25
+ * @typedef {object} Audiobook
26
+ * @property {string} url - The URL of the audiobook.
27
+ * @property {string} bookFormat - The format of the audiobook.
28
+ * @property {string} name - The name of the audiobook.
29
+ * @property {string} description - The description of the audiobook.
30
+ * @property {string} isbn - The ISBN of the audiobook.
31
+ * @property {string} image - The image of the audiobook.
32
+ * @property {string} abridged - Whether the audiobook is abridged.
33
+ * @property {Person[]} author - The author of the audiobook.
34
+ * @property {Person[]} readBy - The person who read the audiobook.
35
+ * @property {string} publisher - The publisher of the audiobook.
36
+ * @property {string} datePublished - The date the audiobook was published.
37
+ * @property {string} inLanguage - The language of the audiobook.
38
+ * @property {string} duration - The duration of the audiobook.
39
+ * @property {string[]} regionsAllowed - The regions allowed for the audiobook.
40
+ * @property {object} offers - The offers for the audiobook.
41
+ * @property {object} workExample - The work example for the audiobook.
42
+ */
43
+ /**
44
+ * Formats the description by removing HTML tags and contents inside them.
45
+ * @param {string} description - The description to be formatted.
46
+ * @returns {string} The formatted description.
47
+ */
48
+ export function formatDescription(description: string): string;
49
+ export type Book = import('../index.js').Book;
50
+ export type AxiosRequestConfig = import('axios').AxiosRequestConfig;
51
+ export type Person = {
52
+ /**
53
+ * - The name of the person.
54
+ */
55
+ name: string;
56
+ };
57
+ export type Audiobook = {
58
+ /**
59
+ * - The URL of the audiobook.
60
+ */
61
+ url: string;
62
+ /**
63
+ * - The format of the audiobook.
64
+ */
65
+ bookFormat: string;
66
+ /**
67
+ * - The name of the audiobook.
68
+ */
69
+ name: string;
70
+ /**
71
+ * - The description of the audiobook.
72
+ */
73
+ description: string;
74
+ /**
75
+ * - The ISBN of the audiobook.
76
+ */
77
+ isbn: string;
78
+ /**
79
+ * - The image of the audiobook.
80
+ */
81
+ image: string;
82
+ /**
83
+ * - Whether the audiobook is abridged.
84
+ */
85
+ abridged: string;
86
+ /**
87
+ * - The author of the audiobook.
88
+ */
89
+ author: Person[];
90
+ /**
91
+ * - The person who read the audiobook.
92
+ */
93
+ readBy: Person[];
94
+ /**
95
+ * - The publisher of the audiobook.
96
+ */
97
+ publisher: string;
98
+ /**
99
+ * - The date the audiobook was published.
100
+ */
101
+ datePublished: string;
102
+ /**
103
+ * - The language of the audiobook.
104
+ */
105
+ inLanguage: string;
106
+ /**
107
+ * - The duration of the audiobook.
108
+ */
109
+ duration: string;
110
+ /**
111
+ * - The regions allowed for the audiobook.
112
+ */
113
+ regionsAllowed: string[];
114
+ /**
115
+ * - The offers for the audiobook.
116
+ */
117
+ offers: object;
118
+ /**
119
+ * - The work example for the audiobook.
120
+ */
121
+ workExample: object;
122
+ };
123
+ //# sourceMappingURL=librofm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"librofm.d.ts","sourceRoot":"","sources":["librofm.js"],"names":[],"mappings":"AAGA;;;GAGG;AAEH;;;;;GAKG;AACH,qCAJW,MAAM,GACJ,QAAQ,IAAI,CAAC,CAezB;AAED;;;;;;GAMG;AACH,kCALW,MAAM,QACN,MAAM,OACN,MAAM,GACJ,QAAQ,IAAI,CAAC,CAgCzB;AAED;;;GAGG;AACH;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;;GAIG;AACH,+CAHW,MAAM,GACJ,MAAM,CAiBlB;mBA3GY,OAAO,aAAa,EAAE,IAAI;iCAC1B,OAAO,OAAO,EAAE,kBAAkB;;;;;UAgEjC,MAAM;;;;;;SAIN,MAAM;;;;gBACN,MAAM;;;;UACN,MAAM;;;;iBACN,MAAM;;;;UACN,MAAM;;;;WACN,MAAM;;;;cACN,MAAM;;;;YACN,MAAM,EAAE;;;;YACR,MAAM,EAAE;;;;eACR,MAAM;;;;mBACN,MAAM;;;;gBACN,MAAM;;;;cACN,MAAM;;;;oBACN,MAAM,EAAE;;;;YACR,MAAM;;;;iBACN,MAAM"}
@@ -0,0 +1,134 @@
1
+ import { LIBROFM_API_BASE, LIBROFM_API_BOOK } from "../provider-resolvers.js";
2
+ import axios from "axios";
3
+
4
+ /**
5
+ * @typedef {import('../index.js').Book} Book
6
+ * @typedef {import('axios').AxiosRequestConfig} AxiosRequestConfig
7
+ */
8
+
9
+ /**
10
+ * Resolves book information from Libro.fm using the provided ISBN.
11
+ * @param {string} isbn - The ISBN of the book.
12
+ * @returns {Promise<Book>} The book information retrieved from the API.
13
+ * @throws {Error} If the API response code is not 200, or if no books are found with the provided ISBN, or if no volume information is found for the book.
14
+ */
15
+ export async function resolveLibroFm(isbn) {
16
+ const url = `${LIBROFM_API_BASE}${LIBROFM_API_BOOK}/${isbn}`;
17
+
18
+ const response = await axios.get(url);
19
+ try {
20
+ if (response.status !== 200) {
21
+ throw new Error(`Unable to get ${url}: ${response.status}`);
22
+ }
23
+ return standardize(response.data, isbn, url);
24
+ } catch (error) {
25
+ throw new Error(error.message);
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Standardizes a book object by extracting relevant information from the provided book object.
31
+ * @param {string} data - The data to be standardized.
32
+ * @param {string} isbn - The book's ISBN.
33
+ * @param {string} url - The URL of the book.
34
+ * @returns {Promise<Book>} The standardized book object.
35
+ */
36
+ export async function standardize(data, isbn, url) {
37
+ // Use a regular expression to extract the JSON
38
+ const regex = /<script type="application\/ld\+json">(.*?)<\/script>/s;
39
+ const match = data.match(regex);
40
+ if (!match) {
41
+ throw new Error(`No information found for ${url}`);
42
+ }
43
+
44
+ /**
45
+ * @type {Audiobook}
46
+ */
47
+ const book = JSON.parse(match[1]);
48
+ const standardBook = {
49
+ title: book.name,
50
+ authors: book.author.map((author) => author.name),
51
+ description: formatDescription(book.description),
52
+ format: book.bookFormat.includes("Audiobook")
53
+ ? "audiobook"
54
+ : book.bookFormat,
55
+ categories: extractGenres(data),
56
+ thumbnail: book.image,
57
+ link: book.url,
58
+ publisher: book.publisher,
59
+ publishedDate: book.datePublished,
60
+ language: book.inLanguage,
61
+ isbn,
62
+ bookProvider: "Libro.fm",
63
+ };
64
+
65
+ return standardBook;
66
+ }
67
+
68
+ /**
69
+ * @typedef {object} Person
70
+ * @property {string} name - The name of the person.
71
+ */
72
+ /**
73
+ * @typedef {object} Audiobook
74
+ * @property {string} url - The URL of the audiobook.
75
+ * @property {string} bookFormat - The format of the audiobook.
76
+ * @property {string} name - The name of the audiobook.
77
+ * @property {string} description - The description of the audiobook.
78
+ * @property {string} isbn - The ISBN of the audiobook.
79
+ * @property {string} image - The image of the audiobook.
80
+ * @property {string} abridged - Whether the audiobook is abridged.
81
+ * @property {Person[]} author - The author of the audiobook.
82
+ * @property {Person[]} readBy - The person who read the audiobook.
83
+ * @property {string} publisher - The publisher of the audiobook.
84
+ * @property {string} datePublished - The date the audiobook was published.
85
+ * @property {string} inLanguage - The language of the audiobook.
86
+ * @property {string} duration - The duration of the audiobook.
87
+ * @property {string[]} regionsAllowed - The regions allowed for the audiobook.
88
+ * @property {object} offers - The offers for the audiobook.
89
+ * @property {object} workExample - The work example for the audiobook.
90
+ */
91
+
92
+ /**
93
+ * Formats the description by removing HTML tags and contents inside them.
94
+ * @param {string} description - The description to be formatted.
95
+ * @returns {string} The formatted description.
96
+ */
97
+ export function formatDescription(description) {
98
+ if (!description) return "";
99
+ // Replace <br> with a space
100
+ description = description.replaceAll("<br>", " ");
101
+ // Replace <b>—</b> with a dash
102
+ description = description.replaceAll("<b>—</b>", "—");
103
+ // Remove bold tags and contents
104
+ description = description.replaceAll(/<b>.*?<\/b>/g, "");
105
+ // Remove all other html elements
106
+ description = description.replaceAll(/<.*?>/g, "");
107
+ // Trim
108
+ description = description.trim();
109
+ // Remove extra spaces
110
+ description = description.replaceAll(/\s{2,}/g, " ");
111
+ return description;
112
+ }
113
+
114
+ /**
115
+ * Extracts the genres from the given text.
116
+ * @param {string} text - The text to extract genres from.
117
+ * @returns {string[]} The extracted genres.
118
+ */
119
+ function extractGenres(text) {
120
+ const regex = /<div class="audiobook-genres">\s*([\S\s]*?)\s*<\/div>/;
121
+ const match = text.match(regex);
122
+ if (!match) {
123
+ return [];
124
+ }
125
+
126
+ const linkRegex = /<a href="\/genres\/[^"]*">(.*?)<\/a>/g;
127
+ const genres = [];
128
+ let linkMatch;
129
+ while ((linkMatch = linkRegex.exec(match[1])) !== null) {
130
+ genres.push(linkMatch[1]);
131
+ }
132
+
133
+ return genres;
134
+ }
@@ -112,7 +112,7 @@ export async function standardize(book, isbn) {
112
112
  authors,
113
113
  description,
114
114
  pageCount: book.number_of_pages,
115
- printType: "BOOK",
115
+ format: "book",
116
116
  categories: subjects,
117
117
  thumbnail: `https://covers.openlibrary.org/b/id/${book.covers[0]}-L.jpg`,
118
118
  link: book.key