diff --git a/client/components/modals/edit-tabs/Match.vue b/client/components/modals/edit-tabs/Match.vue index d2f5ab13..0cb932a2 100644 --- a/client/components/modals/edit-tabs/Match.vue +++ b/client/components/modals/edit-tabs/Match.vue @@ -49,6 +49,10 @@ +
+ + +
@@ -65,6 +69,18 @@
+
+ + +
+
+ + +
+
Update
@@ -96,6 +112,10 @@ export default { { text: 'Open Library', value: 'openlibrary' + }, + { + text: 'Audible', + value: 'audible' } ], provider: 'google', @@ -107,10 +127,13 @@ export default { subtitle: true, cover: true, author: true, + narrator: true, description: true, isbn: true, publisher: true, - publishYear: true + publishYear: true, + series: true, + volumeNumber: true, } } }, @@ -169,10 +192,13 @@ export default { subtitle: true, cover: true, author: true, + narrator: true, description: true, isbn: true, publisher: true, - publishYear: true + publishYear: true, + series: true, + volumeNumber: true, } if (this.audiobook.id !== this.audiobookId) { diff --git a/package.json b/package.json index ab579d13..5fb3a3fc 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "read-chunk": "^3.1.0", "recursive-readdir-async": "^1.1.8", "socket.io": "^4.1.3", + "string-strip-html": "^8.3.0", "watcher": "^1.2.0", "xml2js": "^0.4.23" }, diff --git a/server/BookFinder.js b/server/BookFinder.js index acf00b20..538ae377 100644 --- a/server/BookFinder.js +++ b/server/BookFinder.js @@ -1,6 +1,7 @@ const OpenLibrary = require('./providers/OpenLibrary') const LibGen = require('./providers/LibGen') const GoogleBooks = require('./providers/GoogleBooks') +const Audible = require('./providers/Audible') const Logger = require('./Logger') const { levenshteinDistance } = require('./utils/index') @@ -9,6 +10,7 @@ class BookFinder { this.openLibrary = new OpenLibrary() this.libGen = new LibGen() this.googleBooks = new GoogleBooks() + this.audible = new Audible() this.verbose = false } @@ -156,6 +158,13 @@ class BookFinder { return books } + async getAudibleResults(title, author, maxTitleDistance, maxAuthorDistance) { + var books = await this.audible.search(title, author); + if (this.verbose) Logger.debug(`Audible Book Search Results: ${books.length || 0}`) + if (!books) return [] + return books + } + async search(provider, title, author, options = {}) { var books = [] var maxTitleDistance = !isNaN(options.titleDistance) ? Number(options.titleDistance) : 4 @@ -164,6 +173,8 @@ class BookFinder { if (provider === 'google') { return this.getGoogleBooksResults(title, author, maxTitleDistance, maxAuthorDistance) + } else if (provider === 'audible') { + return this.getAudibleResults(title, author, maxTitleDistance, maxAuthorDistance) } else if (provider === 'libgen') { books = await this.getLibGenResults(title, author, maxTitleDistance, maxAuthorDistance) } else if (provider === 'openlibrary') { diff --git a/server/providers/Audible.js b/server/providers/Audible.js new file mode 100644 index 00000000..9138ce22 --- /dev/null +++ b/server/providers/Audible.js @@ -0,0 +1,51 @@ +const axios = require('axios') +const {stripHtml} = require('string-strip-html') +const Logger = require('../Logger') + +class Audible { + constructor() { } + + cleanResult(item) { + var { title, subtitle, asin, authors, narrators, publisher_name, publisher_summary, release_date, series, product_images } = item; + + var firstSeries = series && series.length > 0 ? series[0] : null; + + return { + title, + subtitle: subtitle || null, + author: authors ? authors.map(({ name }) => name).join(', ') : null, + narrator: narrators ? narrators.map(({ name }) => name).join(', ') : null, + publisher: publisher_name, + publishYear: release_date ? release_date.split('-')[0] : null, + description: stripHtml(publisher_summary).result, + cover: this.getBestImageLink(product_images), + asin, + series: firstSeries ? firstSeries.title : null, + volumeNumber: firstSeries ? firstSeries.sequence : null + } + } + + getBestImageLink(images) { + var keys = Object.keys(images); + return images[keys[keys.length - 1]]; + } + + async search(title, author) { + var queryString = `response_groups=rating,series,contributors,product_desc,media,product_extended_attrs` + + `&image_sizes=500,1024,2000&num_results=25&products_sort_by=Relevance&title=${title}`; + if (author) queryString += `&author=${author}` + var url = `https://api.audible.com/1.0/catalog/products?${queryString}` + Logger.debug(`[Audible] Search url: ${url}`) + var items = await axios.get(url).then((res) => { + if (!res || !res.data || !res.data.products) return [] + return res.data.products + }).catch(error => { + Logger.error('[Audible] search error', error) + return [] + }) + Logger.debug(JSON.stringify(items.map(item => this.cleanResult(item)))) + return items.map(item => this.cleanResult(item)) + } +} + +module.exports = Audible \ No newline at end of file