mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-07-15 13:51:35 +00:00
automatically quickmatch new authors
This commit is contained in:
parent
753ae3d7dc
commit
060cfe6e86
4 changed files with 97 additions and 58 deletions
|
|
@ -8,6 +8,7 @@ const Database = require('../Database')
|
||||||
const CacheManager = require('../managers/CacheManager')
|
const CacheManager = require('../managers/CacheManager')
|
||||||
const CoverManager = require('../managers/CoverManager')
|
const CoverManager = require('../managers/CoverManager')
|
||||||
const AuthorFinder = require('../finders/AuthorFinder')
|
const AuthorFinder = require('../finders/AuthorFinder')
|
||||||
|
const Scanner = require('../scanner/Scanner')
|
||||||
|
|
||||||
const { reqSupportsWebp } = require('../utils/index')
|
const { reqSupportsWebp } = require('../utils/index')
|
||||||
|
|
||||||
|
|
@ -191,54 +192,21 @@ class AuthorController {
|
||||||
res.sendStatus(200)
|
res.sendStatus(200)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// POST api/authors/:id/match
|
||||||
async match(req, res) {
|
async match(req, res) {
|
||||||
let authorData = null
|
var author = req.author
|
||||||
const region = req.body.region || 'us'
|
var options = req.body || {}
|
||||||
if (req.body.asin) {
|
var matchResult = await Scanner.quickMatchAuthor(author, options)
|
||||||
authorData = await AuthorFinder.findAuthorByASIN(req.body.asin, region)
|
if (!matchResult) {
|
||||||
} else {
|
|
||||||
authorData = await AuthorFinder.findAuthorByName(req.body.q, region)
|
|
||||||
}
|
|
||||||
if (!authorData) {
|
|
||||||
return res.status(404).send('Author not found')
|
return res.status(404).send('Author not found')
|
||||||
}
|
}
|
||||||
Logger.debug(`[AuthorController] match author with "${req.body.q || req.body.asin}"`, authorData)
|
if (matchResult.updated) {
|
||||||
|
matchResult.author.updatedAt = Date.now()
|
||||||
let hasUpdates = false
|
await Database.updateAuthor(matchResult.author)
|
||||||
if (authorData.asin && req.author.asin !== authorData.asin) {
|
const numBooks = await Database.libraryItemModel.getForAuthor(matchResult.author).length
|
||||||
req.author.asin = authorData.asin
|
SocketAuthority.emitter('author_updated', matchResult.author.toJSONExpanded(numBooks))
|
||||||
hasUpdates = true
|
|
||||||
}
|
}
|
||||||
|
res.json(matchResult)
|
||||||
// Only updates image if there was no image before or the author ASIN was updated
|
|
||||||
if (authorData.image && (!req.author.imagePath || hasUpdates)) {
|
|
||||||
await CacheManager.purgeImageCache(req.author.id)
|
|
||||||
|
|
||||||
const imageData = await AuthorFinder.saveAuthorImage(req.author.id, authorData.image)
|
|
||||||
if (imageData) {
|
|
||||||
req.author.imagePath = imageData.path
|
|
||||||
hasUpdates = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (authorData.description && req.author.description !== authorData.description) {
|
|
||||||
req.author.description = authorData.description
|
|
||||||
hasUpdates = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasUpdates) {
|
|
||||||
req.author.updatedAt = Date.now()
|
|
||||||
|
|
||||||
await Database.updateAuthor(req.author)
|
|
||||||
|
|
||||||
const numBooks = await Database.libraryItemModel.getForAuthor(req.author).length
|
|
||||||
SocketAuthority.emitter('author_updated', req.author.toJSONExpanded(numBooks))
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
updated: hasUpdates,
|
|
||||||
author: req.author
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET api/authors/:id/image
|
// GET api/authors/:id/image
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
const { DataTypes, Model, where, fn, col } = require('sequelize')
|
const { DataTypes, Model, where, fn, col } = require('sequelize')
|
||||||
|
const { nameToLastFirst } = require('../utils/parsers/parseNameString')
|
||||||
|
|
||||||
const oldAuthor = require('../objects/entities/Author')
|
const oldAuthor = require('../objects/entities/Author')
|
||||||
|
|
||||||
|
|
@ -166,6 +167,12 @@ class Author extends Model {
|
||||||
onDelete: 'CASCADE'
|
onDelete: 'CASCADE'
|
||||||
})
|
})
|
||||||
Author.belongsTo(library)
|
Author.belongsTo(library)
|
||||||
|
|
||||||
|
Author.addHook('beforeCreate', async (author) => {
|
||||||
|
if (!author || author.lastFirst) return
|
||||||
|
author.lastFirst = nameToLastFirst(author.name)
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
module.exports = Author
|
module.exports = Author
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,12 @@ const LibraryFile = require('../objects/files/LibraryFile')
|
||||||
const SocketAuthority = require('../SocketAuthority')
|
const SocketAuthority = require('../SocketAuthority')
|
||||||
const fsExtra = require("../libs/fsExtra")
|
const fsExtra = require("../libs/fsExtra")
|
||||||
const BookFinder = require('../finders/BookFinder')
|
const BookFinder = require('../finders/BookFinder')
|
||||||
|
const Author = require('../objects/entities/Author')
|
||||||
|
|
||||||
const LibraryScan = require("./LibraryScan")
|
const LibraryScan = require("./LibraryScan")
|
||||||
const OpfFileScanner = require('./OpfFileScanner')
|
const OpfFileScanner = require('./OpfFileScanner')
|
||||||
const AbsMetadataFileScanner = require('./AbsMetadataFileScanner')
|
const AbsMetadataFileScanner = require('./AbsMetadataFileScanner')
|
||||||
|
const Scanner = require('../scanner/Scanner')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Metadata for books pulled from files
|
* Metadata for books pulled from files
|
||||||
|
|
@ -192,11 +194,15 @@ class BookScanner {
|
||||||
libraryScan.addLog(LogLevel.DEBUG, `Updating book "${bookMetadata.title}" added author "${authorName}"`)
|
libraryScan.addLog(LogLevel.DEBUG, `Updating book "${bookMetadata.title}" added author "${authorName}"`)
|
||||||
authorsUpdated = true
|
authorsUpdated = true
|
||||||
} else {
|
} else {
|
||||||
const newAuthor = await Database.authorModel.create({
|
let author = new Author()
|
||||||
name: authorName,
|
author.setData({ name: authorName }, libraryItemData.libraryId)
|
||||||
lastFirst: parseNameString.nameToLastFirst(authorName),
|
let authorOptions = {}
|
||||||
libraryId: libraryItemData.libraryId
|
authorOptions.region = libraryScan.library.provider.includes('.') ? libraryScan.library.provider.split('.').pop() : 'us'
|
||||||
})
|
let authorData = await Scanner.quickMatchAuthor(author, authorOptions)
|
||||||
|
if (authorData) {
|
||||||
|
author = authorData.author
|
||||||
|
}
|
||||||
|
const newAuthor = await Database.authorModel.create(author)
|
||||||
await media.addAuthor(newAuthor)
|
await media.addAuthor(newAuthor)
|
||||||
Database.addAuthorToFilterData(libraryItemData.libraryId, newAuthor.name, newAuthor.id)
|
Database.addAuthorToFilterData(libraryItemData.libraryId, newAuthor.name, newAuthor.id)
|
||||||
libraryScan.addLog(LogLevel.DEBUG, `Updating book "${bookMetadata.title}" added new author "${authorName}"`)
|
libraryScan.addLog(LogLevel.DEBUG, `Updating book "${bookMetadata.title}" added new author "${authorName}"`)
|
||||||
|
|
@ -416,12 +422,16 @@ class BookScanner {
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// New author
|
// New author
|
||||||
bookObject.bookAuthors.push({
|
let author = new Author()
|
||||||
author: {
|
author.setData({ name: authorName }, libraryItemData.libraryId)
|
||||||
libraryId: libraryItemData.libraryId,
|
let authorOptions = {}
|
||||||
name: authorName,
|
authorOptions.region = libraryScan.library.provider.includes('.') ? libraryScan.library.provider.split('.').pop() : 'us'
|
||||||
lastFirst: parseNameString.nameToLastFirst(authorName)
|
let authorData = await Scanner.quickMatchAuthor(author, authorOptions)
|
||||||
|
if (authorData) {
|
||||||
|
author = authorData.author
|
||||||
}
|
}
|
||||||
|
bookObject.bookAuthors.push({
|
||||||
|
author: author.toJSON()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,14 @@ const Author = require('../objects/entities/Author')
|
||||||
const Series = require('../objects/entities/Series')
|
const Series = require('../objects/entities/Series')
|
||||||
const LibraryScanner = require('./LibraryScanner')
|
const LibraryScanner = require('./LibraryScanner')
|
||||||
const CoverManager = require('../managers/CoverManager')
|
const CoverManager = require('../managers/CoverManager')
|
||||||
|
const CacheManager = require('../managers/CacheManager')
|
||||||
|
const AuthorFinder = require('../finders/AuthorFinder')
|
||||||
|
|
||||||
class Scanner {
|
class Scanner {
|
||||||
constructor() { }
|
constructor() { }
|
||||||
|
|
||||||
async quickMatchLibraryItem(libraryItem, options = {}) {
|
async quickMatchLibraryItem(libraryItem, options = {}) {
|
||||||
var provider = options.provider || 'google'
|
options.provider = options.provider || 'google'
|
||||||
var searchTitle = options.title || libraryItem.media.metadata.title
|
var searchTitle = options.title || libraryItem.media.metadata.title
|
||||||
var searchAuthor = options.author || libraryItem.media.metadata.authorName
|
var searchAuthor = options.author || libraryItem.media.metadata.authorName
|
||||||
var overrideDefaults = options.overrideDefaults || false
|
var overrideDefaults = options.overrideDefaults || false
|
||||||
|
|
@ -36,10 +38,10 @@ class Scanner {
|
||||||
var searchISBN = options.isbn || libraryItem.media.metadata.isbn
|
var searchISBN = options.isbn || libraryItem.media.metadata.isbn
|
||||||
var searchASIN = options.asin || libraryItem.media.metadata.asin
|
var searchASIN = options.asin || libraryItem.media.metadata.asin
|
||||||
|
|
||||||
var results = await BookFinder.search(provider, searchTitle, searchAuthor, searchISBN, searchASIN, { maxFuzzySearches: 2 })
|
var results = await BookFinder.search(options.provider, searchTitle, searchAuthor, searchISBN, searchASIN, { maxFuzzySearches: 2 })
|
||||||
if (!results.length) {
|
if (!results.length) {
|
||||||
return {
|
return {
|
||||||
warning: `No ${provider} match found`
|
warning: `No ${options.provider} match found`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var matchData = results[0]
|
var matchData = results[0]
|
||||||
|
|
@ -60,7 +62,7 @@ class Scanner {
|
||||||
var results = await PodcastFinder.search(searchTitle)
|
var results = await PodcastFinder.search(searchTitle)
|
||||||
if (!results.length) {
|
if (!results.length) {
|
||||||
return {
|
return {
|
||||||
warning: `No ${provider} match found`
|
warning: `No ${options.provider} match found`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var matchData = results[0]
|
var matchData = results[0]
|
||||||
|
|
@ -189,6 +191,13 @@ class Scanner {
|
||||||
if (!author) {
|
if (!author) {
|
||||||
author = new Author()
|
author = new Author()
|
||||||
author.setData({ name: authorName }, libraryItem.libraryId)
|
author.setData({ name: authorName }, libraryItem.libraryId)
|
||||||
|
let authorOptions = {}
|
||||||
|
authorOptions.region = options.provider.includes('.') ? options.provider.split('.').pop() : 'us'
|
||||||
|
let authorData = await this.quickMatchAuthor(author, authorOptions)
|
||||||
|
if (authorData) {
|
||||||
|
author = authorData.author
|
||||||
|
}
|
||||||
|
|
||||||
await Database.createAuthor(author)
|
await Database.createAuthor(author)
|
||||||
SocketAuthority.emitter('author_added', author.toJSON())
|
SocketAuthority.emitter('author_added', author.toJSON())
|
||||||
// Update filter data
|
// Update filter data
|
||||||
|
|
@ -362,5 +371,50 @@ class Scanner {
|
||||||
LibraryScanner.librariesScanning = LibraryScanner.librariesScanning.filter(ls => ls.id !== library.id)
|
LibraryScanner.librariesScanning = LibraryScanner.librariesScanning.filter(ls => ls.id !== library.id)
|
||||||
SocketAuthority.emitter('scan_complete', libraryScan.getScanEmitData)
|
SocketAuthority.emitter('scan_complete', libraryScan.getScanEmitData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search providers for author
|
||||||
|
* @param {Author} author
|
||||||
|
* @param {{region:string, asin:string, q:string}} options
|
||||||
|
*/
|
||||||
|
async quickMatchAuthor(author, options = {}) {
|
||||||
|
let authorData = null
|
||||||
|
const region = options.region || 'us'
|
||||||
|
if (options.asin) {
|
||||||
|
authorData = await AuthorFinder.findAuthorByASIN(options.asin, region)
|
||||||
|
} else {
|
||||||
|
authorData = await AuthorFinder.findAuthorByName(options.q || author.name, region)
|
||||||
|
}
|
||||||
|
if (!authorData) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
Logger.debug(`[Scanner] match author from with "${options.q || options.asin}"`, authorData)
|
||||||
|
|
||||||
|
let hasUpdated = false;
|
||||||
|
['asin', 'name', 'description'].forEach(key => {
|
||||||
|
if (authorData[key] && author[key] !== authorData[key]) {
|
||||||
|
author[key] = authorData[key];
|
||||||
|
hasUpdated = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Only updates image if there was no image before or the author ASIN was updated
|
||||||
|
if (authorData.image && (!author.imagePath || (authorData.asin && author.asin !== authorData.asin))) {
|
||||||
|
await CacheManager.purgeImageCache(author.id)
|
||||||
|
|
||||||
|
const imageData = await AuthorFinder.saveAuthorImage(author.id, authorData.image)
|
||||||
|
if (imageData) {
|
||||||
|
author.imagePath = imageData.path
|
||||||
|
hasUpdated = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
updated: hasUpdated,
|
||||||
|
author: author
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
module.exports = new Scanner()
|
module.exports = new Scanner()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue