mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-01-08 00:08:14 +01:00
Update:Validate ASIN for author, chapter and match requests
This commit is contained in:
parent
ee501f70ed
commit
a018374d26
@ -9,7 +9,7 @@ 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 { reqSupportsWebp } = require('../utils/index')
|
const { reqSupportsWebp, isValidASIN } = require('../utils/index')
|
||||||
|
|
||||||
const naturalSort = createNewSortInstance({
|
const naturalSort = createNewSortInstance({
|
||||||
comparer: new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }).compare
|
comparer: new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }).compare
|
||||||
@ -252,7 +252,7 @@ class AuthorController {
|
|||||||
async match(req, res) {
|
async match(req, res) {
|
||||||
let authorData = null
|
let authorData = null
|
||||||
const region = req.body.region || 'us'
|
const region = req.body.region || 'us'
|
||||||
if (req.body.asin) {
|
if (req.body.asin && isValidASIN(req.body.asin.toUpperCase?.())) {
|
||||||
authorData = await AuthorFinder.findAuthorByASIN(req.body.asin, region)
|
authorData = await AuthorFinder.findAuthorByASIN(req.body.asin, region)
|
||||||
} else {
|
} else {
|
||||||
authorData = await AuthorFinder.findAuthorByName(req.body.q, region)
|
authorData = await AuthorFinder.findAuthorByName(req.body.q, region)
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
const Logger = require("../Logger")
|
const Logger = require('../Logger')
|
||||||
const BookFinder = require('../finders/BookFinder')
|
const BookFinder = require('../finders/BookFinder')
|
||||||
const PodcastFinder = require('../finders/PodcastFinder')
|
const PodcastFinder = require('../finders/PodcastFinder')
|
||||||
const AuthorFinder = require('../finders/AuthorFinder')
|
const AuthorFinder = require('../finders/AuthorFinder')
|
||||||
const MusicFinder = require('../finders/MusicFinder')
|
const MusicFinder = require('../finders/MusicFinder')
|
||||||
const Database = require("../Database")
|
const Database = require('../Database')
|
||||||
|
const { isValidASIN } = require('../utils')
|
||||||
|
|
||||||
class SearchController {
|
class SearchController {
|
||||||
constructor() { }
|
constructor() {}
|
||||||
|
|
||||||
async findBooks(req, res) {
|
async findBooks(req, res) {
|
||||||
const id = req.query.id
|
const id = req.query.id
|
||||||
@ -63,6 +64,9 @@ class SearchController {
|
|||||||
|
|
||||||
async findChapters(req, res) {
|
async findChapters(req, res) {
|
||||||
const asin = req.query.asin
|
const asin = req.query.asin
|
||||||
|
if (!isValidASIN(asin.toUpperCase())) {
|
||||||
|
return res.json({ error: 'Invalid ASIN' })
|
||||||
|
}
|
||||||
const region = (req.query.region || 'us').toLowerCase()
|
const region = (req.query.region || 'us').toLowerCase()
|
||||||
const chapterData = await BookFinder.findChapters(asin, region)
|
const chapterData = await BookFinder.findChapters(asin, region)
|
||||||
if (!chapterData) {
|
if (!chapterData) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
const axios = require('axios').default
|
const axios = require('axios').default
|
||||||
const htmlSanitizer = require('../utils/htmlSanitizer')
|
const htmlSanitizer = require('../utils/htmlSanitizer')
|
||||||
const Logger = require('../Logger')
|
const Logger = require('../Logger')
|
||||||
|
const { isValidASIN } = require('../utils/index')
|
||||||
|
|
||||||
class Audible {
|
class Audible {
|
||||||
#responseTimeout = 30000
|
#responseTimeout = 30000
|
||||||
@ -81,16 +82,6 @@ class Audible {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if a search title matches an ASIN. Supports lowercase letters
|
|
||||||
*
|
|
||||||
* @param {string} title
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
isProbablyAsin(title) {
|
|
||||||
return /^[0-9A-Za-z]{10}$/.test(title)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {string} asin
|
* @param {string} asin
|
||||||
@ -137,11 +128,11 @@ class Audible {
|
|||||||
if (!timeout || isNaN(timeout)) timeout = this.#responseTimeout
|
if (!timeout || isNaN(timeout)) timeout = this.#responseTimeout
|
||||||
|
|
||||||
let items
|
let items
|
||||||
if (asin) {
|
if (asin && isValidASIN(asin.toUpperCase())) {
|
||||||
items = [await this.asinSearch(asin, region, timeout)]
|
items = [await this.asinSearch(asin, region, timeout)]
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!items && this.isProbablyAsin(title)) {
|
if (!items && isValidASIN(title.toUpperCase())) {
|
||||||
items = [await this.asinSearch(title, region, timeout)]
|
items = [await this.asinSearch(title, region, timeout)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
const axios = require('axios').default
|
const axios = require('axios').default
|
||||||
const { levenshteinDistance } = require('../utils/index')
|
|
||||||
const Logger = require('../Logger')
|
|
||||||
const Throttle = require('p-throttle')
|
const Throttle = require('p-throttle')
|
||||||
|
const Logger = require('../Logger')
|
||||||
|
const { levenshteinDistance } = require('../utils/index')
|
||||||
|
const { isValidASIN } = require('../utils/index')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef AuthorSearchObj
|
* @typedef AuthorSearchObj
|
||||||
@ -66,13 +67,19 @@ class Audnexus {
|
|||||||
* @returns {Promise<AuthorSearchObj>}
|
* @returns {Promise<AuthorSearchObj>}
|
||||||
*/
|
*/
|
||||||
authorRequest(asin, region) {
|
authorRequest(asin, region) {
|
||||||
asin = encodeURIComponent(asin)
|
if (!isValidASIN(asin?.toUpperCase?.())) {
|
||||||
const regionQuery = region ? `?region=${region}` : ''
|
Logger.error(`[Audnexus] Invalid ASIN ${asin}`)
|
||||||
const authorRequestUrl = `${this.baseUrl}/authors/${asin}${regionQuery}`
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
asin = encodeURIComponent(asin.toUpperCase())
|
||||||
|
|
||||||
|
const authorRequestUrl = new URL(`${this.baseUrl}/authors/${asin}`)
|
||||||
|
if (region) authorRequestUrl.searchParams.set('region', region)
|
||||||
|
|
||||||
Logger.info(`[Audnexus] Searching for author "${authorRequestUrl}"`)
|
Logger.info(`[Audnexus] Searching for author "${authorRequestUrl}"`)
|
||||||
|
|
||||||
return this._processRequest(this.limiter(() => axios.get(authorRequestUrl)))
|
return this._processRequest(this.limiter(() => axios.get(authorRequestUrl.toString())))
|
||||||
.then((res) => res.data)
|
.then((res) => res.data)
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
Logger.error(`[Audnexus] Author request failed for ${asin}`, error)
|
Logger.error(`[Audnexus] Author request failed for ${asin}`, error)
|
||||||
@ -135,10 +142,20 @@ class Audnexus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} asin
|
||||||
|
* @param {string} region
|
||||||
|
* @returns {Promise<Object>}
|
||||||
|
*/
|
||||||
getChaptersByASIN(asin, region) {
|
getChaptersByASIN(asin, region) {
|
||||||
Logger.debug(`[Audnexus] Get chapters for ASIN ${asin}/${region}`)
|
Logger.debug(`[Audnexus] Get chapters for ASIN ${asin}/${region}`)
|
||||||
|
|
||||||
return this._processRequest(this.limiter(() => axios.get(`${this.baseUrl}/books/${asin}/chapters?region=${region}`)))
|
asin = encodeURIComponent(asin.toUpperCase())
|
||||||
|
const chaptersRequestUrl = new URL(`${this.baseUrl}/books/${asin}/chapters`)
|
||||||
|
if (region) chaptersRequestUrl.searchParams.set('region', region)
|
||||||
|
|
||||||
|
return this._processRequest(this.limiter(() => axios.get(chaptersRequestUrl.toString())))
|
||||||
.then((res) => res.data)
|
.then((res) => res.data)
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
Logger.error(`[Audnexus] Chapter ASIN request failed for ${asin}/${region}`, error)
|
Logger.error(`[Audnexus] Chapter ASIN request failed for ${asin}/${region}`, error)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
const Path = require('path')
|
const Path = require('path')
|
||||||
const uuid = require('uuid')
|
const uuid = require('uuid')
|
||||||
const Logger = require('../Logger')
|
const Logger = require('../Logger')
|
||||||
const { parseString } = require("xml2js")
|
const { parseString } = require('xml2js')
|
||||||
const areEquivalent = require('./areEquivalent')
|
const areEquivalent = require('./areEquivalent')
|
||||||
|
|
||||||
const levenshteinDistance = (str1, str2, caseSensitive = false) => {
|
const levenshteinDistance = (str1, str2, caseSensitive = false) => {
|
||||||
@ -11,8 +11,9 @@ const levenshteinDistance = (str1, str2, caseSensitive = false) => {
|
|||||||
str1 = str1.toLowerCase()
|
str1 = str1.toLowerCase()
|
||||||
str2 = str2.toLowerCase()
|
str2 = str2.toLowerCase()
|
||||||
}
|
}
|
||||||
const track = Array(str2.length + 1).fill(null).map(() =>
|
const track = Array(str2.length + 1)
|
||||||
Array(str1.length + 1).fill(null))
|
.fill(null)
|
||||||
|
.map(() => Array(str1.length + 1).fill(null))
|
||||||
for (let i = 0; i <= str1.length; i += 1) {
|
for (let i = 0; i <= str1.length; i += 1) {
|
||||||
track[0][i] = i
|
track[0][i] = i
|
||||||
}
|
}
|
||||||
@ -25,7 +26,7 @@ const levenshteinDistance = (str1, str2, caseSensitive = false) => {
|
|||||||
track[j][i] = Math.min(
|
track[j][i] = Math.min(
|
||||||
track[j][i - 1] + 1, // deletion
|
track[j][i - 1] + 1, // deletion
|
||||||
track[j - 1][i] + 1, // insertion
|
track[j - 1][i] + 1, // insertion
|
||||||
track[j - 1][i - 1] + indicator, // substitution
|
track[j - 1][i - 1] + indicator // substitution
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,7 +139,10 @@ module.exports.toNumber = (val, fallback = 0) => {
|
|||||||
module.exports.cleanStringForSearch = (str) => {
|
module.exports.cleanStringForSearch = (str) => {
|
||||||
if (!str) return ''
|
if (!str) return ''
|
||||||
// Remove ' . ` " ,
|
// Remove ' . ` " ,
|
||||||
return str.toLowerCase().replace(/[\'\.\`\",]/g, '').trim()
|
return str
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/[\'\.\`\",]/g, '')
|
||||||
|
.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTitleParts = (title) => {
|
const getTitleParts = (title) => {
|
||||||
@ -235,3 +239,14 @@ module.exports.isUUID = (str) => {
|
|||||||
if (!str || typeof str !== 'string') return false
|
if (!str || typeof str !== 'string') return false
|
||||||
return uuid.validate(str)
|
return uuid.validate(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a string is a valid ASIN
|
||||||
|
*
|
||||||
|
* @param {string} str
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
module.exports.isValidASIN = (str) => {
|
||||||
|
if (!str || typeof str !== 'string') return false
|
||||||
|
return /^[A-Z0-9]{10}$/.test(str)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user