mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-08-04 02:05:06 +02:00
This commit is contained in:
parent
7c1789a7c2
commit
ad4dad1c29
18 changed files with 311 additions and 55 deletions
|
@ -6,6 +6,8 @@ const Logger = require('./Logger')
|
|||
const { isObject } = require('./utils/index')
|
||||
const audioFileScanner = require('./utils/audioFileScanner')
|
||||
|
||||
const BookFinder = require('./BookFinder')
|
||||
|
||||
const Library = require('./objects/Library')
|
||||
const User = require('./objects/User')
|
||||
|
||||
|
@ -24,6 +26,8 @@ class ApiController {
|
|||
this.clientEmitter = clientEmitter
|
||||
this.MetadataPath = MetadataPath
|
||||
|
||||
this.bookFinder = new BookFinder()
|
||||
|
||||
this.router = express()
|
||||
this.init()
|
||||
}
|
||||
|
@ -51,6 +55,7 @@ class ApiController {
|
|||
this.router.patch('/audiobook/:id/tracks', this.updateAudiobookTracks.bind(this))
|
||||
this.router.post('/audiobook/:id/cover', this.uploadAudiobookCover.bind(this))
|
||||
this.router.patch('/audiobook/:id/coverfile', this.updateAudiobookCoverFromFile.bind(this))
|
||||
this.router.get('/audiobook/:id/match', this.matchAudiobookBook.bind(this))
|
||||
this.router.patch('/audiobook/:id', this.updateAudiobook.bind(this))
|
||||
|
||||
this.router.patch('/match/:id', this.match.bind(this))
|
||||
|
@ -85,8 +90,12 @@ class ApiController {
|
|||
this.router.get('/scantracks/:id', this.scanAudioTrackNums.bind(this))
|
||||
}
|
||||
|
||||
find(req, res) {
|
||||
this.scanner.find(req, res)
|
||||
async find(req, res) {
|
||||
var provider = req.query.provider || 'google'
|
||||
var title = req.query.title || ''
|
||||
var author = req.query.author || ''
|
||||
var results = await this.bookFinder.search(provider, title, author)
|
||||
res.json(results)
|
||||
}
|
||||
|
||||
findCovers(req, res) {
|
||||
|
@ -497,6 +506,18 @@ class ApiController {
|
|||
else res.status(200).send('No update was made to cover')
|
||||
}
|
||||
|
||||
async matchAudiobookBook(req, res) {
|
||||
var audiobook = this.db.audiobooks.find(a => a.id === req.params.id)
|
||||
if (!audiobook) return res.sendStatus(404)
|
||||
|
||||
var provider = req.query.provider || 'google'
|
||||
var excludeAuthor = req.query.excludeAuthor === '1'
|
||||
var authorSearch = excludeAuthor ? null : audiobook.authorFL
|
||||
|
||||
var results = await this.bookFinder.search(provider, audiobook.title, authorSearch)
|
||||
res.json(results)
|
||||
}
|
||||
|
||||
async updateAudiobook(req, res) {
|
||||
if (!req.user.canUpdate) {
|
||||
Logger.warn('User attempted to update without permission', req.user)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const OpenLibrary = require('./providers/OpenLibrary')
|
||||
const LibGen = require('./providers/LibGen')
|
||||
const GoogleBooks = require('./providers/GoogleBooks')
|
||||
const Logger = require('./Logger')
|
||||
const { levenshteinDistance } = require('./utils/index')
|
||||
|
||||
|
@ -7,6 +8,7 @@ class BookFinder {
|
|||
constructor() {
|
||||
this.openLibrary = new OpenLibrary()
|
||||
this.libGen = new LibGen()
|
||||
this.googleBooks = new GoogleBooks()
|
||||
|
||||
this.verbose = false
|
||||
}
|
||||
|
@ -143,13 +145,26 @@ class BookFinder {
|
|||
return booksFiltered
|
||||
}
|
||||
|
||||
async getGoogleBooksResults(title, author, maxTitleDistance, maxAuthorDistance) {
|
||||
var books = await this.googleBooks.search(title, author)
|
||||
if (this.verbose) Logger.debug(`GoogleBooks Book Search Results: ${books.length || 0}`)
|
||||
if (books.errorCode) {
|
||||
Logger.error(`GoogleBooks Search Error ${books.errorCode}`)
|
||||
return []
|
||||
}
|
||||
// Google has good sort
|
||||
return books
|
||||
}
|
||||
|
||||
async search(provider, title, author, options = {}) {
|
||||
var books = []
|
||||
var maxTitleDistance = !isNaN(options.titleDistance) ? Number(options.titleDistance) : 4
|
||||
var maxAuthorDistance = !isNaN(options.authorDistance) ? Number(options.authorDistance) : 4
|
||||
Logger.debug(`Cover Search: title: "${title}", author: "${author}", provider: ${provider}`)
|
||||
|
||||
if (provider === 'libgen') {
|
||||
if (provider === 'google') {
|
||||
return this.getGoogleBooksResults(title, author, maxTitleDistance, maxAuthorDistance)
|
||||
} else if (provider === 'libgen') {
|
||||
books = await this.getLibGenResults(title, author, maxTitleDistance, maxAuthorDistance)
|
||||
} else if (provider === 'openlibrary') {
|
||||
books = await this.getOpenLibResults(title, author, maxTitleDistance, maxAuthorDistance)
|
||||
|
|
|
@ -10,7 +10,6 @@ const { comparePaths, getIno } = require('./utils/index')
|
|||
const { secondsToTimestamp } = require('./utils/fileUtils')
|
||||
const { ScanResult, CoverDestination } = require('./utils/constants')
|
||||
|
||||
// Classes
|
||||
const BookFinder = require('./BookFinder')
|
||||
const Audiobook = require('./objects/Audiobook')
|
||||
|
||||
|
|
|
@ -91,6 +91,10 @@ class Audiobook {
|
|||
return this.book ? this.book.authorLF : null
|
||||
}
|
||||
|
||||
get authorFL() {
|
||||
return this.book ? this.book.authorFL : null
|
||||
}
|
||||
|
||||
get genres() {
|
||||
return this.book ? this.book.genres || [] : []
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
const fs = require('fs-extra')
|
||||
const Path = require('path')
|
||||
const Logger = require('../Logger')
|
||||
const parseAuthors = require('../utils/parseAuthors')
|
||||
|
@ -16,6 +15,7 @@ class Book {
|
|||
this.publishYear = null
|
||||
this.publisher = null
|
||||
this.description = null
|
||||
this.isbn = null
|
||||
this.cover = null
|
||||
this.coverFullPath = null
|
||||
this.genres = []
|
||||
|
@ -56,6 +56,7 @@ class Book {
|
|||
this.publishYear = book.publishYear
|
||||
this.publisher = book.publisher
|
||||
this.description = book.description
|
||||
this.isbn = book.isbn || null
|
||||
this.cover = book.cover
|
||||
this.coverFullPath = book.coverFullPath || null
|
||||
this.genres = book.genres
|
||||
|
@ -78,6 +79,7 @@ class Book {
|
|||
publishYear: this.publishYear,
|
||||
publisher: this.publisher,
|
||||
description: this.description,
|
||||
isbn: this.isbn,
|
||||
cover: this.cover,
|
||||
coverFullPath: this.coverFullPath,
|
||||
genres: this.genres,
|
||||
|
@ -116,6 +118,7 @@ class Book {
|
|||
this.volumeNumber = data.volumeNumber || null
|
||||
this.publishYear = data.publishYear || null
|
||||
this.description = data.description || null
|
||||
this.isbn = data.isbn || null
|
||||
this.cover = data.cover || null
|
||||
this.coverFullPath = data.coverFullPath || null
|
||||
this.genres = data.genres || []
|
||||
|
|
50
server/providers/GoogleBooks.js
Normal file
50
server/providers/GoogleBooks.js
Normal file
|
@ -0,0 +1,50 @@
|
|||
const axios = require('axios')
|
||||
const Logger = require('../Logger')
|
||||
|
||||
class GoogleBooks {
|
||||
constructor() { }
|
||||
|
||||
extractIsbn(industryIdentifiers) {
|
||||
if (!industryIdentifiers || !industryIdentifiers.length) return null
|
||||
|
||||
var isbnObj = industryIdentifiers.find(i => i.type === 'ISBN_13') || industryIdentifiers.find(i => i.type === 'ISBN_10')
|
||||
if (isbnObj && isbnObj.identifier) return isbnObj.identifier
|
||||
return null
|
||||
}
|
||||
|
||||
cleanResult(item) {
|
||||
var { id, volumeInfo } = item
|
||||
if (!volumeInfo) return null
|
||||
var { title, subtitle, authors, publisher, publisherDate, description, industryIdentifiers, categories, imageLinks } = volumeInfo
|
||||
|
||||
return {
|
||||
id,
|
||||
title,
|
||||
subtitle: subtitle || null,
|
||||
author: authors ? authors.join(', ') : null,
|
||||
publisher,
|
||||
publishYear: publisherDate ? publisherDate.split('-')[0] : null,
|
||||
description,
|
||||
cover: imageLinks && imageLinks.thumbnail ? imageLinks.thumbnail : null,
|
||||
genres: categories ? categories.join(', ') : null,
|
||||
isbn: this.extractIsbn(industryIdentifiers)
|
||||
}
|
||||
}
|
||||
|
||||
async search(title, author) {
|
||||
var queryString = `q=intitle:${title}`
|
||||
if (author) queryString += `+inauthor:${author}`
|
||||
var url = `https://www.googleapis.com/books/v1/volumes?${queryString}`
|
||||
Logger.debug(`[GoogleBooks] Search url: ${url}`)
|
||||
var items = await axios.get(url).then((res) => {
|
||||
if (!res || !res.data || !res.data.items) return []
|
||||
return res.data.items
|
||||
}).catch(error => {
|
||||
Logger.error('[GoogleBooks] Volume search error', error)
|
||||
return []
|
||||
})
|
||||
return items.map(item => this.cleanResult(item))
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GoogleBooks
|
|
@ -51,12 +51,21 @@ class OpenLibrary {
|
|||
}
|
||||
}
|
||||
|
||||
parsePublishYear(doc, worksData) {
|
||||
if (doc.first_publish_year && !isNaN(doc.first_publish_year)) return doc.first_publish_year
|
||||
if (worksData.first_publish_date) {
|
||||
var year = worksData.first_publish_date.split('-')[0]
|
||||
if (!isNaN(year)) return year
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
async cleanSearchDoc(doc) {
|
||||
var worksData = await this.getWorksData(doc.key)
|
||||
return {
|
||||
title: doc.title,
|
||||
author: doc.author_name ? doc.author_name.join(', ') : null,
|
||||
year: doc.first_publish_year,
|
||||
publishYear: this.parsePublishYear(doc, worksData),
|
||||
edition: doc.cover_edition_key,
|
||||
cover: doc.cover_edition_key ? `https://covers.openlibrary.org/b/OLID/${doc.cover_edition_key}-L.jpg` : null,
|
||||
...worksData
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue