mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2024-12-20 19:06:06 +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 AuthorFinder = require('../finders/AuthorFinder')
|
||||
|
||||
const { reqSupportsWebp } = require('../utils/index')
|
||||
const { reqSupportsWebp, isValidASIN } = require('../utils/index')
|
||||
|
||||
const naturalSort = createNewSortInstance({
|
||||
comparer: new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }).compare
|
||||
@ -252,7 +252,7 @@ class AuthorController {
|
||||
async match(req, res) {
|
||||
let authorData = null
|
||||
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)
|
||||
} else {
|
||||
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 PodcastFinder = require('../finders/PodcastFinder')
|
||||
const AuthorFinder = require('../finders/AuthorFinder')
|
||||
const MusicFinder = require('../finders/MusicFinder')
|
||||
const Database = require("../Database")
|
||||
const Database = require('../Database')
|
||||
const { isValidASIN } = require('../utils')
|
||||
|
||||
class SearchController {
|
||||
constructor() { }
|
||||
constructor() {}
|
||||
|
||||
async findBooks(req, res) {
|
||||
const id = req.query.id
|
||||
@ -37,9 +38,9 @@ class SearchController {
|
||||
|
||||
/**
|
||||
* Find podcast RSS feeds given a term
|
||||
*
|
||||
* @param {import('express').Request} req
|
||||
* @param {import('express').Response} res
|
||||
*
|
||||
* @param {import('express').Request} req
|
||||
* @param {import('express').Response} res
|
||||
*/
|
||||
async findPodcasts(req, res) {
|
||||
const term = req.query.term
|
||||
@ -63,6 +64,9 @@ class SearchController {
|
||||
|
||||
async findChapters(req, res) {
|
||||
const asin = req.query.asin
|
||||
if (!isValidASIN(asin.toUpperCase())) {
|
||||
return res.json({ error: 'Invalid ASIN' })
|
||||
}
|
||||
const region = (req.query.region || 'us').toLowerCase()
|
||||
const chapterData = await BookFinder.findChapters(asin, region)
|
||||
if (!chapterData) {
|
||||
@ -78,4 +82,4 @@ class SearchController {
|
||||
})
|
||||
}
|
||||
}
|
||||
module.exports = new SearchController()
|
||||
module.exports = new SearchController()
|
||||
|
@ -1,6 +1,7 @@
|
||||
const axios = require('axios').default
|
||||
const htmlSanitizer = require('../utils/htmlSanitizer')
|
||||
const Logger = require('../Logger')
|
||||
const { isValidASIN } = require('../utils/index')
|
||||
|
||||
class Audible {
|
||||
#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
|
||||
@ -137,11 +128,11 @@ class Audible {
|
||||
if (!timeout || isNaN(timeout)) timeout = this.#responseTimeout
|
||||
|
||||
let items
|
||||
if (asin) {
|
||||
if (asin && isValidASIN(asin.toUpperCase())) {
|
||||
items = [await this.asinSearch(asin, region, timeout)]
|
||||
}
|
||||
|
||||
if (!items && this.isProbablyAsin(title)) {
|
||||
if (!items && isValidASIN(title.toUpperCase())) {
|
||||
items = [await this.asinSearch(title, region, timeout)]
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
const axios = require('axios').default
|
||||
const { levenshteinDistance } = require('../utils/index')
|
||||
const Logger = require('../Logger')
|
||||
const Throttle = require('p-throttle')
|
||||
const Logger = require('../Logger')
|
||||
const { levenshteinDistance } = require('../utils/index')
|
||||
const { isValidASIN } = require('../utils/index')
|
||||
|
||||
/**
|
||||
* @typedef AuthorSearchObj
|
||||
@ -66,13 +67,19 @@ class Audnexus {
|
||||
* @returns {Promise<AuthorSearchObj>}
|
||||
*/
|
||||
authorRequest(asin, region) {
|
||||
asin = encodeURIComponent(asin)
|
||||
const regionQuery = region ? `?region=${region}` : ''
|
||||
const authorRequestUrl = `${this.baseUrl}/authors/${asin}${regionQuery}`
|
||||
if (!isValidASIN(asin?.toUpperCase?.())) {
|
||||
Logger.error(`[Audnexus] Invalid ASIN ${asin}`)
|
||||
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}"`)
|
||||
|
||||
return this._processRequest(this.limiter(() => axios.get(authorRequestUrl)))
|
||||
return this._processRequest(this.limiter(() => axios.get(authorRequestUrl.toString())))
|
||||
.then((res) => res.data)
|
||||
.catch((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) {
|
||||
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)
|
||||
.catch((error) => {
|
||||
Logger.error(`[Audnexus] Chapter ASIN request failed for ${asin}/${region}`, error)
|
||||
|
@ -1,7 +1,7 @@
|
||||
const Path = require('path')
|
||||
const uuid = require('uuid')
|
||||
const Logger = require('../Logger')
|
||||
const { parseString } = require("xml2js")
|
||||
const { parseString } = require('xml2js')
|
||||
const areEquivalent = require('./areEquivalent')
|
||||
|
||||
const levenshteinDistance = (str1, str2, caseSensitive = false) => {
|
||||
@ -11,8 +11,9 @@ const levenshteinDistance = (str1, str2, caseSensitive = false) => {
|
||||
str1 = str1.toLowerCase()
|
||||
str2 = str2.toLowerCase()
|
||||
}
|
||||
const track = Array(str2.length + 1).fill(null).map(() =>
|
||||
Array(str1.length + 1).fill(null))
|
||||
const track = Array(str2.length + 1)
|
||||
.fill(null)
|
||||
.map(() => Array(str1.length + 1).fill(null))
|
||||
for (let i = 0; i <= str1.length; i += 1) {
|
||||
track[0][i] = i
|
||||
}
|
||||
@ -25,7 +26,7 @@ const levenshteinDistance = (str1, str2, caseSensitive = false) => {
|
||||
track[j][i] = Math.min(
|
||||
track[j][i - 1] + 1, // deletion
|
||||
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) => {
|
||||
if (!str) return ''
|
||||
// Remove ' . ` " ,
|
||||
return str.toLowerCase().replace(/[\'\.\`\",]/g, '').trim()
|
||||
return str
|
||||
.toLowerCase()
|
||||
.replace(/[\'\.\`\",]/g, '')
|
||||
.trim()
|
||||
}
|
||||
|
||||
const getTitleParts = (title) => {
|
||||
@ -156,7 +160,7 @@ const getTitleParts = (title) => {
|
||||
/**
|
||||
* Remove sortingPrefixes from title
|
||||
* @example "The Good Book" => "Good Book"
|
||||
* @param {string} title
|
||||
* @param {string} title
|
||||
* @returns {string}
|
||||
*/
|
||||
module.exports.getTitleIgnorePrefix = (title) => {
|
||||
@ -164,9 +168,9 @@ module.exports.getTitleIgnorePrefix = (title) => {
|
||||
}
|
||||
|
||||
/**
|
||||
* Put sorting prefix at the end of title
|
||||
* Put sorting prefix at the end of title
|
||||
* @example "The Good Book" => "Good Book, The"
|
||||
* @param {string} title
|
||||
* @param {string} title
|
||||
* @returns {string}
|
||||
*/
|
||||
module.exports.getTitlePrefixAtEnd = (title) => {
|
||||
@ -178,8 +182,8 @@ module.exports.getTitlePrefixAtEnd = (title) => {
|
||||
* to lower case for only ascii characters
|
||||
* used to handle sqlite that doesnt support unicode lower
|
||||
* @see https://github.com/advplyr/audiobookshelf/issues/2187
|
||||
*
|
||||
* @param {string} str
|
||||
*
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
module.exports.asciiOnlyToLowerCase = (str) => {
|
||||
@ -200,8 +204,8 @@ module.exports.asciiOnlyToLowerCase = (str) => {
|
||||
/**
|
||||
* Escape string used in RegExp
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
|
||||
*
|
||||
* @param {string} str
|
||||
*
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
module.exports.escapeRegExp = (str) => {
|
||||
@ -211,8 +215,8 @@ module.exports.escapeRegExp = (str) => {
|
||||
|
||||
/**
|
||||
* Validate url string with URL class
|
||||
*
|
||||
* @param {string} rawUrl
|
||||
*
|
||||
* @param {string} rawUrl
|
||||
* @returns {string} null if invalid
|
||||
*/
|
||||
module.exports.validateUrl = (rawUrl) => {
|
||||
@ -227,11 +231,22 @@ module.exports.validateUrl = (rawUrl) => {
|
||||
|
||||
/**
|
||||
* Check if a string is a valid UUID
|
||||
*
|
||||
* @param {string} str
|
||||
*
|
||||
* @param {string} str
|
||||
* @returns {boolean}
|
||||
*/
|
||||
module.exports.isUUID = (str) => {
|
||||
if (!str || typeof str !== 'string') return false
|
||||
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