+
diff --git a/client/package.json b/client/package.json
index 8d8f63ae..426ad94f 100644
--- a/client/package.json
+++ b/client/package.json
@@ -1,6 +1,6 @@
{
"name": "audiobookshelf-client",
- "version": "1.4.2",
+ "version": "1.4.3",
"description": "Audiobook manager and player",
"main": "index.js",
"scripts": {
diff --git a/package.json b/package.json
index 33133f9c..91460660 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "audiobookshelf",
- "version": "1.4.2",
+ "version": "1.4.3",
"description": "Self-hosted audiobook server for managing and playing audiobooks",
"main": "index.js",
"scripts": {
diff --git a/server/ApiController.js b/server/ApiController.js
index 1db25ef2..55df8479 100644
--- a/server/ApiController.js
+++ b/server/ApiController.js
@@ -30,6 +30,7 @@ class ApiController {
this.router.get('/find/:method', this.find.bind(this))
this.router.get('/libraries', this.getLibraries.bind(this))
+ this.router.get('/library/:id/search', this.searchLibrary.bind(this))
this.router.get('/library/:id', this.getLibrary.bind(this))
this.router.delete('/library/:id', this.deleteLibrary.bind(this))
this.router.patch('/library/:id', this.updateLibrary.bind(this))
@@ -97,6 +98,53 @@ class ApiController {
res.json(libraries)
}
+ searchLibrary(req, res) {
+ var library = this.db.libraries.find(lib => lib.id === req.params.id)
+ if (!library) {
+ return res.status(404).send('Library not found')
+ }
+ if (!req.query.q) {
+ return res.status(400).send('No query string')
+ }
+ var maxResults = req.query.max || 3
+
+ var bookMatches = []
+ var authorMatches = {}
+ var seriesMatches = {}
+
+ var audiobooksInLibrary = this.db.audiobooks.filter(ab => ab.libraryId === library.id)
+ audiobooksInLibrary.forEach((ab) => {
+ var queryResult = ab.searchQuery(req.query.q)
+ if (queryResult.book) {
+ bookMatches.push({
+ audiobook: ab,
+ matchKey: queryResult.book
+ })
+ }
+ if (queryResult.author && !authorMatches[queryResult.author]) {
+ authorMatches[queryResult.author] = {
+ author: queryResult.author
+ }
+ }
+ if (queryResult.series) {
+ if (!seriesMatches[queryResult.series]) {
+ seriesMatches[queryResult.series] = {
+ series: queryResult.series,
+ audiobooks: [ab]
+ }
+ } else {
+ seriesMatches[queryResult.series].audiobooks.push(ab)
+ }
+ }
+ })
+
+ res.json({
+ audiobooks: bookMatches.slice(0, maxResults),
+ authors: Object.values(authorMatches).slice(0, maxResults),
+ series: Object.values(seriesMatches).slice(0, maxResults)
+ })
+ }
+
getLibrary(req, res) {
var library = this.db.libraries.find(lib => lib.id === req.params.id)
if (!library) {
@@ -563,8 +611,14 @@ class ApiController {
if (!settingsUpdate || !isObject(settingsUpdate)) {
return res.status(500).send('Invalid settings update object')
}
+
var madeUpdates = this.db.serverSettings.update(settingsUpdate)
if (madeUpdates) {
+ // If backup schedule is updated - update backup manager
+ if (settingsUpdate.backupSchedule !== undefined) {
+ this.backupManager.updateCronSchedule()
+ }
+
await this.db.updateEntity('settings', this.db.serverSettings)
}
return res.json({
diff --git a/server/BackupManager.js b/server/BackupManager.js
index bacb730b..9f3f3cbd 100644
--- a/server/BackupManager.js
+++ b/server/BackupManager.js
@@ -21,6 +21,8 @@ class BackupManager {
this.Gid = Gid
this.db = db
+ this.scheduleTask = null
+
this.backups = []
}
@@ -28,7 +30,7 @@ class BackupManager {
return this.db.serverSettings || {}
}
- async init(overrideCron = null) {
+ async init() {
var backupsDirExists = await fs.pathExists(this.BackupPath)
if (!backupsDirExists) {
await fs.ensureDir(this.BackupPath)
@@ -36,16 +38,34 @@ class BackupManager {
}
await this.loadBackups()
+ this.scheduleCron()
+ }
+ scheduleCron() {
if (!this.serverSettings.backupSchedule) {
Logger.info(`[BackupManager] Auto Backups are disabled`)
return
}
try {
- var cronSchedule = overrideCron || this.serverSettings.backupSchedule
- cron.schedule(cronSchedule, this.runBackup.bind(this))
+ var cronSchedule = this.serverSettings.backupSchedule
+ this.scheduleTask = cron.schedule(cronSchedule, this.runBackup.bind(this))
} catch (error) {
- Logger.error(`[BackupManager] Failed to schedule backup cron ${this.serverSettings.backupSchedule}`)
+ Logger.error(`[BackupManager] Failed to schedule backup cron ${this.serverSettings.backupSchedule}`, error)
+ }
+ }
+
+ updateCronSchedule() {
+ if (this.scheduleTask && !this.serverSettings.backupSchedule) {
+ Logger.info(`[BackupManager] Disabling backup schedule`)
+ if (this.scheduleTask.destroy) this.scheduleTask.destroy()
+ this.scheduleTask = null
+ } else if (!this.scheduleTask && this.serverSettings.backupSchedule) {
+ Logger.info(`[BackupManager] Starting backup schedule ${this.serverSettings.backupSchedule}`)
+ this.scheduleCron()
+ } else if (this.serverSettings.backupSchedule) {
+ Logger.info(`[BackupManager] Restarting backup schedule ${this.serverSettings.backupSchedule}`)
+ if (this.scheduleTask.destroy) this.scheduleTask.destroy()
+ this.scheduleCron()
}
}
diff --git a/server/objects/Audiobook.js b/server/objects/Audiobook.js
index fe753a0a..d012194c 100644
--- a/server/objects/Audiobook.js
+++ b/server/objects/Audiobook.js
@@ -623,6 +623,10 @@ class Audiobook {
return this.book.isSearchMatch(search.toLowerCase().trim())
}
+ searchQuery(search) {
+ return this.book.getQueryMatches(search.toLowerCase().trim())
+ }
+
getAudioFileByIno(ino) {
return this.audioFiles.find(af => af.ino === ino)
}
diff --git a/server/objects/Book.js b/server/objects/Book.js
index 0b511e2a..27631285 100644
--- a/server/objects/Book.js
+++ b/server/objects/Book.js
@@ -5,7 +5,6 @@ const parseAuthors = require('../utils/parseAuthors')
class Book {
constructor(book = null) {
- this.olid = null
this.title = null
this.subtitle = null
this.author = null
@@ -46,7 +45,6 @@ class Book {
}
construct(book) {
- this.olid = book.olid
this.title = book.title
this.subtitle = book.subtitle || null
this.author = book.author
@@ -69,7 +67,6 @@ class Book {
toJSON() {
return {
- olid: this.olid,
title: this.title,
subtitle: this.subtitle,
author: this.author,
@@ -111,7 +108,6 @@ class Book {
}
setData(data) {
- this.olid = data.olid || null
this.title = data.title || null
this.subtitle = data.subtitle || null
this.author = data.author || null
@@ -217,6 +213,17 @@ class Book {
return this._title.toLowerCase().includes(search) || this._subtitle.toLowerCase().includes(search) || this._author.toLowerCase().includes(search) || this._series.toLowerCase().includes(search)
}
+ getQueryMatches(search) {
+ var titleMatch = this._title.toLowerCase().includes(search) || this._subtitle.toLowerCase().includes(search)
+ var authorMatch = this._author.toLowerCase().includes(search)
+ var seriesMatch = this._series.toLowerCase().includes(search)
+ return {
+ book: titleMatch ? 'title' : authorMatch ? 'author' : seriesMatch ? 'series' : false,
+ author: authorMatch ? this._author : false,
+ series: seriesMatch ? this._series : false
+ }
+ }
+
setDetailsFromFileMetadata(audioFileMetadata) {
const MetadataMapArray = [
{
diff --git a/server/objects/Library.js b/server/objects/Library.js
index 3e7b4d4f..38a62a95 100644
--- a/server/objects/Library.js
+++ b/server/objects/Library.js
@@ -5,6 +5,7 @@ class Library {
this.id = null
this.name = null
this.folders = []
+ this.icon = 'database'
this.lastScan = 0
@@ -24,6 +25,8 @@ class Library {
this.id = library.id
this.name = library.name
this.folders = (library.folders || []).map(f => new Folder(f))
+ this.icon = library.icon || 'database'
+
this.createdAt = library.createdAt
this.lastUpdate = library.lastUpdate
}
@@ -33,6 +36,7 @@ class Library {
id: this.id,
name: this.name,
folders: (this.folders || []).map(f => f.toJSON()),
+ icon: this.icon,
createdAt: this.createdAt,
lastUpdate: this.lastUpdate
}
@@ -55,6 +59,7 @@ class Library {
return newFolder
})
}
+ this.icon = data.icon || 'database'
this.createdAt = Date.now()
this.lastUpdate = Date.now()
}