mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-01-01 16:49:38 +00:00
New data model for global search input and search page
This commit is contained in:
parent
30f15d3575
commit
ea9ec13845
16 changed files with 241 additions and 88 deletions
|
|
@ -355,65 +355,62 @@ class LibraryController {
|
|||
|
||||
// GET: Global library search
|
||||
search(req, res) {
|
||||
var library = req.library
|
||||
if (!req.query.q) {
|
||||
return res.status(400).send('No query string')
|
||||
}
|
||||
var libraryItems = req.libraryItems
|
||||
var maxResults = req.query.limit && !isNaN(req.query.limit) ? Number(req.query.limit) : 12
|
||||
|
||||
var bookMatches = []
|
||||
var itemMatches = []
|
||||
var authorMatches = {}
|
||||
var seriesMatches = {}
|
||||
var tagMatches = {}
|
||||
|
||||
var audiobooksInLibrary = this.db.audiobooks.filter(ab => ab.libraryId === library.id)
|
||||
audiobooksInLibrary.forEach((ab) => {
|
||||
var queryResult = ab.searchQuery(req.query.q)
|
||||
if (queryResult.book) {
|
||||
var bookMatchObj = {
|
||||
audiobook: ab.toJSONExpanded(),
|
||||
matchKey: queryResult.book,
|
||||
matchText: queryResult.bookMatchText
|
||||
}
|
||||
bookMatches.push(bookMatchObj)
|
||||
libraryItems.forEach((li) => {
|
||||
var queryResult = li.searchQuery(req.query.q)
|
||||
if (queryResult.matchKey) {
|
||||
itemMatches.push({
|
||||
libraryItem: li,
|
||||
matchKey: queryResult.matchKey,
|
||||
matchText: queryResult.matchText
|
||||
})
|
||||
}
|
||||
if (queryResult.authors) {
|
||||
queryResult.authors.forEach((author) => {
|
||||
if (!authorMatches[author]) {
|
||||
authorMatches[author] = {
|
||||
author: author,
|
||||
numBooks: 1
|
||||
}
|
||||
if (queryResult.series && queryResult.series.length) {
|
||||
queryResult.series.forEach((se) => {
|
||||
if (!seriesMatches[se.id]) {
|
||||
var _series = this.db.series.find(_se => _se.id === se.id)
|
||||
if (_series) seriesMatches[se.id] = { series: _series.toJSON(), books: [li.toJSON()] }
|
||||
} else {
|
||||
authorMatches[author].numBooks++
|
||||
seriesMatches[se.id].books.push(li.toJSON())
|
||||
}
|
||||
})
|
||||
}
|
||||
if (queryResult.series) {
|
||||
if (!seriesMatches[queryResult.series]) {
|
||||
seriesMatches[queryResult.series] = {
|
||||
series: queryResult.series,
|
||||
audiobooks: [ab.toJSONExpanded()]
|
||||
if (queryResult.authors && queryResult.authors.length) {
|
||||
queryResult.authors.forEach((au) => {
|
||||
if (!authorMatches[au.id]) {
|
||||
var _author = this.db.authors.find(_au => _au.id === au.id)
|
||||
if (_author) {
|
||||
authorMatches[au.id] = _author.toJSON()
|
||||
authorMatches[au.id].numBooks = 1
|
||||
}
|
||||
} else {
|
||||
authorMatches[au.id].numBooks++
|
||||
}
|
||||
} else {
|
||||
seriesMatches[queryResult.series].audiobooks.push(ab.toJSONExpanded())
|
||||
}
|
||||
})
|
||||
}
|
||||
if (queryResult.tags && queryResult.tags.length) {
|
||||
queryResult.tags.forEach((tag) => {
|
||||
if (!tagMatches[tag]) {
|
||||
tagMatches[tag] = {
|
||||
tag,
|
||||
audiobooks: [ab.toJSONExpanded()]
|
||||
}
|
||||
tagMatches[tag] = { name: tag, books: [li.toJSON()] }
|
||||
} else {
|
||||
tagMatches[tag].audiobooks.push(ab.toJSONExpanded())
|
||||
tagMatches[tag].books.push(li.toJSON())
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
var itemKey = req.library.itemMediaType
|
||||
var results = {
|
||||
audiobooks: bookMatches.slice(0, maxResults),
|
||||
[itemKey]: itemMatches.slice(0, maxResults),
|
||||
tags: Object.values(tagMatches).slice(0, maxResults),
|
||||
authors: Object.values(authorMatches).slice(0, maxResults),
|
||||
series: Object.values(seriesMatches).slice(0, maxResults)
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@ class Library {
|
|||
get folderPaths() {
|
||||
return this.folders.map(f => f.fullPath)
|
||||
}
|
||||
get itemMediaType() {
|
||||
return this.mediaType === 'podcast' ? 'podcast' : 'book'
|
||||
}
|
||||
|
||||
construct(library) {
|
||||
this.id = library.id
|
||||
|
|
|
|||
|
|
@ -401,5 +401,10 @@ class LibraryItem {
|
|||
}
|
||||
return hasUpdated
|
||||
}
|
||||
|
||||
searchQuery(query) {
|
||||
query = query.toLowerCase()
|
||||
return this.media.searchQuery(query)
|
||||
}
|
||||
}
|
||||
module.exports = LibraryItem
|
||||
|
|
@ -313,5 +313,33 @@ class Book {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
searchQuery(query) {
|
||||
var payload = {
|
||||
tags: this.tags.filter(t => t.toLowerCase().includes(query)),
|
||||
series: this.metadata.searchSeries(query),
|
||||
authors: this.metadata.searchAuthors(query),
|
||||
matchKey: null,
|
||||
matchText: null
|
||||
}
|
||||
var metadataMatch = this.metadata.searchQuery(query)
|
||||
if (metadataMatch) {
|
||||
payload.matchKey = metadataMatch.matchKey
|
||||
payload.matchText = metadataMatch.matchText
|
||||
} else {
|
||||
if (payload.authors.length) {
|
||||
payload.matchKey = 'authors'
|
||||
payload.matchText = this.metadata.authorName
|
||||
} else if (payload.series.length) {
|
||||
payload.matchKey = 'series'
|
||||
payload.matchText = this.metadata.seriesName
|
||||
}
|
||||
else if (payload.tags.length) {
|
||||
payload.matchKey = 'tags'
|
||||
payload.matchText = this.tags.join(', ')
|
||||
}
|
||||
}
|
||||
return payload
|
||||
}
|
||||
}
|
||||
module.exports = Book
|
||||
|
|
@ -124,5 +124,10 @@ class Podcast {
|
|||
async syncMetadataFiles(textMetadataFiles, opfMetadataOverrideDetails) {
|
||||
return false
|
||||
}
|
||||
|
||||
searchQuery(query) {
|
||||
var payload = this.metadata.searchQuery(query)
|
||||
return payload || {}
|
||||
}
|
||||
}
|
||||
module.exports = Podcast
|
||||
|
|
@ -91,6 +91,13 @@ class BookMetadata {
|
|||
if (!this.authors.length) return ''
|
||||
return this.authors.map(au => au.name).join(', ')
|
||||
}
|
||||
get seriesName() {
|
||||
if (!this.series.length) return ''
|
||||
return this.series.map(se => {
|
||||
if (!se.sequence) return se.name
|
||||
return `${se.name} #${se.sequence}`
|
||||
}).join(', ')
|
||||
}
|
||||
get narratorName() {
|
||||
return this.narrators.join(', ')
|
||||
}
|
||||
|
|
@ -268,5 +275,24 @@ class BookMetadata {
|
|||
sequence: sequenceTag || ''
|
||||
}]
|
||||
}
|
||||
|
||||
searchSeries(query) {
|
||||
return this.series.filter(se => se.name.toLowerCase().includes(query))
|
||||
}
|
||||
searchAuthors(query) {
|
||||
return this.authors.filter(se => se.name.toLowerCase().includes(query))
|
||||
}
|
||||
searchQuery(query) { // Returns key if match is found
|
||||
var keysToCheck = ['title', 'asin', 'isbn']
|
||||
for (var key of keysToCheck) {
|
||||
if (this[key] && this[key].toLowerCase().includes(query)) {
|
||||
return {
|
||||
matchKey: key,
|
||||
matchText: this[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
module.exports = BookMetadata
|
||||
|
|
@ -47,5 +47,18 @@ class PodcastMetadata {
|
|||
toJSONExpanded() {
|
||||
return this.toJSON()
|
||||
}
|
||||
|
||||
searchQuery(query) { // Returns key if match is found
|
||||
var keysToCheck = ['title', 'artist', 'itunesId', 'itunesArtistId']
|
||||
for (var key of keysToCheck) {
|
||||
if (this[key] && String(this[key]).toLowerCase().includes(query)) {
|
||||
return {
|
||||
matchKey: key,
|
||||
matchText: this[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
module.exports = PodcastMetadata
|
||||
Loading…
Add table
Add a link
Reference in a new issue