Update:Match author use closest name match by levenshtein distance #2624

This commit is contained in:
advplyr 2024-02-18 13:06:51 -06:00
parent 58598bfcf2
commit acf75abdf1
2 changed files with 56 additions and 8 deletions

View File

@ -15,12 +15,19 @@ class AuthorFinder {
return this.audnexus.findAuthorByASIN(asin, region) return this.audnexus.findAuthorByASIN(asin, region)
} }
/**
*
* @param {string} name
* @param {string} region
* @param {Object} [options={}]
* @returns {Promise<import('../providers/Audnexus').AuthorSearchObj>}
*/
async findAuthorByName(name, region, options = {}) { async findAuthorByName(name, region, options = {}) {
if (!name) return null if (!name) return null
const maxLevenshtein = !isNaN(options.maxLevenshtein) ? Number(options.maxLevenshtein) : 3 const maxLevenshtein = !isNaN(options.maxLevenshtein) ? Number(options.maxLevenshtein) : 3
const author = await this.audnexus.findAuthorByName(name, region, maxLevenshtein) const author = await this.audnexus.findAuthorByName(name, region, maxLevenshtein)
if (!author || !author.name) { if (!author?.name) {
return null return null
} }
return author return author

View File

@ -2,15 +2,30 @@ const axios = require('axios')
const { levenshteinDistance } = require('../utils/index') const { levenshteinDistance } = require('../utils/index')
const Logger = require('../Logger') const Logger = require('../Logger')
/**
* @typedef AuthorSearchObj
* @property {string} asin
* @property {string} description
* @property {string} image
* @property {string} name
*/
class Audnexus { class Audnexus {
constructor() { constructor() {
this.baseUrl = 'https://api.audnex.us' this.baseUrl = 'https://api.audnex.us'
} }
/**
*
* @param {string} name
* @param {string} region
* @returns {Promise<{asin:string, name:string}[]>}
*/
authorASINsRequest(name, region) { authorASINsRequest(name, region) {
name = encodeURIComponent(name) const searchParams = new URLSearchParams()
const regionQuery = region ? `&region=${region}` : '' searchParams.set('name', name)
const authorRequestUrl = `${this.baseUrl}/authors?name=${name}${regionQuery}` if (region) searchParams.set('region', region)
const authorRequestUrl = `${this.baseUrl}/authors?${searchParams.toString()}`
Logger.info(`[Audnexus] Searching for author "${authorRequestUrl}"`) Logger.info(`[Audnexus] Searching for author "${authorRequestUrl}"`)
return axios.get(authorRequestUrl).then((res) => { return axios.get(authorRequestUrl).then((res) => {
return res.data || [] return res.data || []
@ -20,6 +35,12 @@ class Audnexus {
}) })
} }
/**
*
* @param {string} asin
* @param {string} region
* @returns {Promise<AuthorSearchObj>}
*/
authorRequest(asin, region) { authorRequest(asin, region) {
asin = encodeURIComponent(asin) asin = encodeURIComponent(asin)
const regionQuery = region ? `?region=${region}` : '' const regionQuery = region ? `?region=${region}` : ''
@ -33,6 +54,12 @@ class Audnexus {
}) })
} }
/**
*
* @param {string} asin
* @param {string} region
* @returns {Promise<AuthorSearchObj>}
*/
async findAuthorByASIN(asin, region) { async findAuthorByASIN(asin, region) {
const author = await this.authorRequest(asin, region) const author = await this.authorRequest(asin, region)
if (!author) { if (!author) {
@ -46,14 +73,28 @@ class Audnexus {
} }
} }
/**
*
* @param {string} name
* @param {string} region
* @param {number} maxLevenshtein
* @returns {Promise<AuthorSearchObj>}
*/
async findAuthorByName(name, region, maxLevenshtein = 3) { async findAuthorByName(name, region, maxLevenshtein = 3) {
Logger.debug(`[Audnexus] Looking up author by name ${name}`) Logger.debug(`[Audnexus] Looking up author by name ${name}`)
const asins = await this.authorASINsRequest(name, region) const authorAsinObjs = await this.authorASINsRequest(name, region)
const matchingAsin = asins.find(obj => levenshteinDistance(obj.name, name) <= maxLevenshtein)
if (!matchingAsin) { let closestMatch = null
authorAsinObjs.forEach((authorAsinObj) => {
authorAsinObj.levenshteinDistance = levenshteinDistance(authorAsinObj.name, name)
if (!closestMatch || closestMatch.levenshteinDistance > authorAsinObj.levenshteinDistance) {
closestMatch = authorAsinObj
}
})
if (!closestMatch || closestMatch.levenshteinDistance > maxLevenshtein) {
return null return null
} }
const author = await this.authorRequest(matchingAsin.asin) const author = await this.authorRequest(closestMatch.asin)
if (!author) { if (!author) {
return null return null
} }