Add search by Open Library
This commit is contained in:
parent
abc1e10b3f
commit
e39fe52e65
|
@ -53,7 +53,7 @@ export class SearchController extends ViewController {
|
|||
|
||||
const searchTerm = this.appState.query.for.trim();
|
||||
|
||||
return fetch(`/api/search?for=${searchTerm}&lang=${this.appState.language}`)
|
||||
return fetch(`/api/search?for=${searchTerm}&by=${this.state.searchBy}&lang=${this.appState.language}&source=${this.state.searchSource}`)
|
||||
.then(response => response.json())
|
||||
.then(responseJSON => {
|
||||
this.state.results = responseJSON;
|
||||
|
|
|
@ -94,10 +94,10 @@ export const searchView = (state, emit, i18n) => {
|
|||
<option value="inventaire" ${controller.state.searchSource === 'inventaire' ? 'selected' : null}>
|
||||
Inventaire
|
||||
</option>
|
||||
<option value="openlibrary" ${controller.state.searchSource === 'openlibrary' ? 'selected' : null}>
|
||||
<option value="openLibrary" ${controller.state.searchSource === 'openLibrary' ? 'selected' : null}>
|
||||
Open Library
|
||||
</option>
|
||||
<option value="bookbrainz" ${controller.state.searchSource === 'bookbrainz' ? 'selected' : null}>
|
||||
<option value="bookBrainz" ${controller.state.searchSource === 'bookBrainz' ? 'selected' : null}>
|
||||
BookBrainz
|
||||
</option>
|
||||
</select>
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
const fetch = require('node-fetch');
|
||||
|
||||
class BooksController {
|
||||
constructor(inventaireDomain, bookURI, language) {
|
||||
this.inventaire = inventaireDomain;
|
||||
constructor(bookSource, bookURI, language) {
|
||||
this.source = bookSource;
|
||||
this.inventaire = 'https://inventaire.io';
|
||||
this.openLibrary = 'https://openlibrary.org';
|
||||
this.bookBrainz = 'https://bookbrainz.org';
|
||||
this.uri = bookURI;
|
||||
this.lang = language;
|
||||
}
|
||||
|
@ -87,7 +90,7 @@ class BooksController {
|
|||
typeof entityObject.image !== 'undefined'
|
||||
? entityObject.image.map(imageId => {
|
||||
return {
|
||||
uri: imageId,
|
||||
uri: imageId.toString(),
|
||||
url: `${this.inventaire}/img/entities/${imageId}`,
|
||||
}
|
||||
})
|
||||
|
@ -132,6 +135,36 @@ class BooksController {
|
|||
};
|
||||
}
|
||||
|
||||
handleOpenLibraryEntity(entityObject) {
|
||||
return {
|
||||
name: (
|
||||
typeof entityObject.title_suggest !== 'undefined'
|
||||
? entityObject.title_suggest
|
||||
: null
|
||||
),
|
||||
description: (
|
||||
typeof entityObject.author_name !== 'undefined'
|
||||
? `${entityObject.type} by ${entityObject.author_name.map(name => name.trim()).join(', ')}`
|
||||
: null
|
||||
),
|
||||
link: (
|
||||
typeof entityObject.key !== 'undefined'
|
||||
? `${this.openLibrary}${entityObject.key}`
|
||||
: null
|
||||
),
|
||||
uri: (
|
||||
typeof entityObject.key !== 'undefined'
|
||||
? entityObject.key.substr(entityObject.key.lastIndexOf('/') + 1)
|
||||
: null
|
||||
),
|
||||
coverId: (
|
||||
typeof entityObject.cover_i !== 'undefined'
|
||||
? entityObject.cover_i.toString()
|
||||
: false
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
async getBookDataFromInventaire() {
|
||||
if (this.uri) {
|
||||
const request = fetch(`${this.inventaire}/api/entities?action=by-uris&uris=${encodeURIComponent(this.uri)}`)
|
||||
|
|
|
@ -3,8 +3,7 @@ const fetch = require('node-fetch');
|
|||
const BooksController = require('./books');
|
||||
|
||||
class SearchController {
|
||||
constructor(inventaireDomain, searchTerm, language = 'en') {
|
||||
this.inventaire = inventaireDomain;
|
||||
constructor(searchTerm, language = 'en') {
|
||||
this.term = searchTerm;
|
||||
this.lang = language;
|
||||
}
|
||||
|
@ -15,7 +14,7 @@ class SearchController {
|
|||
|
||||
quickSearchInventaire() {
|
||||
if (this.hasQuery) {
|
||||
const request = fetch(`${this.inventaire}/api/search?types=works&search=${encodeURIComponent(this.term)}&lang=${encodeURIComponent(this.lang)}&limit=10`)
|
||||
const request = fetch(`https://inventaire.io/api/search?types=works&search=${encodeURIComponent(this.term)}&lang=${encodeURIComponent(this.lang)}&limit=10`)
|
||||
request.catch(exception => {
|
||||
console.error(exception);
|
||||
return {
|
||||
|
@ -32,9 +31,11 @@ class SearchController {
|
|||
}
|
||||
});
|
||||
return json.then(responseJSON => {
|
||||
const booksController = new BooksController('inventaire', undefined, this.lang);
|
||||
|
||||
return responseJSON.results.map(work => {
|
||||
const booksController = new BooksController(this.inventaire, work.uri, this.lang);
|
||||
const bookData = booksController.handleQuickInventaireEntity(work);
|
||||
booksController.uri = bookData.uri; // Update booksController.uri for each book when fetching community data.
|
||||
const communityData = booksController.getCommunityData(5);
|
||||
|
||||
return {
|
||||
|
@ -46,9 +47,9 @@ class SearchController {
|
|||
}
|
||||
}
|
||||
|
||||
searchInventaire() {
|
||||
searchInventaire(searchBy = 'title') {
|
||||
if (this.hasQuery) {
|
||||
const request = fetch(`${this.inventaire}/api/entities?action=search&search=${encodeURIComponent(this.term)}&lang=${encodeURIComponent(this.lang)}`)
|
||||
const request = fetch(`https://inventaire.io/api/entities?action=search&search=${encodeURIComponent(this.term)}&lang=${encodeURIComponent(this.lang)}`)
|
||||
request.catch(exception => {
|
||||
console.error(exception);
|
||||
return {
|
||||
|
@ -65,8 +66,8 @@ class SearchController {
|
|||
}
|
||||
});
|
||||
return json.then(responseJSON => {
|
||||
const booksController = new BooksController('inventaire', undefined, this.lang);
|
||||
return responseJSON.works.map(work => {
|
||||
const booksController = new BooksController(this.inventaire, work.uri, this.lang);
|
||||
const bookData = booksController.handleInventaireEntity(work);
|
||||
const communityData = booksController.getCommunityData(5);
|
||||
|
||||
|
@ -167,44 +168,56 @@ class SearchController {
|
|||
});
|
||||
}
|
||||
|
||||
async searchOpenLibrary() {
|
||||
async searchOpenLibrary(searchBy = 'title') {
|
||||
if (!this.hasQuery) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return fetch('http://openlibrary.org/search.json?q=' + encodeURIComponent(this.term))
|
||||
return fetch(`http://openlibrary.org/search.json?${searchBy}=${encodeURIComponent(this.term)}`)
|
||||
.then(res => res.json())
|
||||
.then(response => {
|
||||
if (!response.hasOwnProperty('docs')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const booksController = new BooksController('openLibrary', undefined, this.lang);
|
||||
|
||||
// Format the response into usable objects
|
||||
const docs = response.docs.map(doc => {
|
||||
return {
|
||||
title: doc.title_suggest.trim(),
|
||||
authors: doc.hasOwnProperty('author_name') ? doc.author_name.map(name => name.trim()) : [],
|
||||
cover: doc.hasOwnProperty('cover_i') ? `//covers.openlibrary.org/b/id/${doc.cover_i}-S.jpg` : false,
|
||||
};
|
||||
return booksController.handleOpenLibraryEntity(doc);
|
||||
});
|
||||
|
||||
// Filter out duplicate items with the same title and author
|
||||
const results = docs.filter((doc, index, allDocs) => {
|
||||
return typeof allDocs.find((filterResult, filterIndex) => {
|
||||
return index !== filterIndex && filterResult.title === doc.title
|
||||
&& JSON.stringify(filterResult.authors) === JSON.stringify(doc.authors);
|
||||
&& filterResult.description === doc.description;
|
||||
}) === 'undefined';
|
||||
}).map(result => {
|
||||
// Find any duplicates in case they have different cover data
|
||||
const duplicates = docs.filter(doc => {
|
||||
return doc.title.toLowerCase() === result.title.toLowerCase() && JSON.stringify(doc.authors) === JSON.stringify(result.authors);
|
||||
return doc.name.toLowerCase() === result.name.toLowerCase() && doc.description === result.description;
|
||||
});
|
||||
result.covers = [];
|
||||
duplicates.forEach(duplicate => {
|
||||
if (duplicate.cover !== false) {
|
||||
result.covers.push(duplicate.cover);
|
||||
if (duplicate.coverId !== false) {
|
||||
result.covers.push({
|
||||
uri: duplicate.coverId,
|
||||
url: `//covers.openlibrary.org/b/id/${duplicate.coverId}-M.jpg`,
|
||||
});
|
||||
}
|
||||
});
|
||||
delete result.coverId;
|
||||
return result;
|
||||
}).map(bookData => {
|
||||
// Use bookController to get community data
|
||||
booksController.uri = bookData.uri; // Update booksController.uri for each book when fetching community data.
|
||||
const communityData = booksController.getCommunityData(5);
|
||||
|
||||
return {
|
||||
...bookData,
|
||||
...communityData,
|
||||
};
|
||||
});
|
||||
|
||||
return results;
|
||||
|
|
|
@ -3,10 +3,27 @@ const SearchController = require('../controllers/search');
|
|||
async function routes(fastify, options) {
|
||||
fastify.get('/api/search', async (request, reply) => {
|
||||
const searchTerm = typeof request.query.for !== 'undefined' ? request.query.for.trim() : '';
|
||||
const searchBy = typeof request.query.by !== 'undefined' ? request.query.by.trim() : 'title';
|
||||
const language = typeof request.query.lang !== 'undefined' ? request.query.lang.trim().split('-')[0] : undefined; // Get base language in cases like 'en-US'
|
||||
const search = new SearchController(fastify.siteConfig.inventaireDomain, searchTerm, language);
|
||||
const searchSource = typeof request.query.source !== 'undefined' ? request.query.source.trim() : undefined; // Get base language in cases like 'en-US'
|
||||
const search = new SearchController(searchTerm, language);
|
||||
|
||||
return await search.quickSearchInventaire();
|
||||
switch (searchSource) {
|
||||
case 'openLibrary': {
|
||||
return await search.searchOpenLibrary(searchBy);
|
||||
}
|
||||
case 'bookBrainz': {
|
||||
return await search.searchOpenLibrary(searchBy);
|
||||
}
|
||||
case 'inventaire':
|
||||
default: {
|
||||
if (searchBy === 'title') {
|
||||
return await search.quickSearchInventaire();
|
||||
} else {
|
||||
return await search.searchInventaire(searchBy);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
fastify.get('/api/search/cover', async (request, reply) => {
|
||||
|
|
Loading…
Reference in New Issue