mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-03-10 00:17:21 +01:00
128 lines
4.0 KiB
JavaScript
128 lines
4.0 KiB
JavaScript
const axios = require('axios').default
|
|
const Database = require('../Database')
|
|
const Logger = require('../Logger')
|
|
const htmlSanitizer = require('../utils/htmlSanitizer')
|
|
|
|
class CustomProviderAdapter {
|
|
#responseTimeout = 30000
|
|
|
|
constructor() {}
|
|
|
|
/**
|
|
*
|
|
* @param {string} title
|
|
* @param {string} author
|
|
* @param {string} isbn
|
|
* @param {string} providerSlug
|
|
* @param {string} mediaType
|
|
* @param {number} [timeout] response timeout in ms
|
|
* @returns {Promise<Object[]>}
|
|
*/
|
|
async search(title, author, isbn, providerSlug, mediaType, timeout = this.#responseTimeout) {
|
|
if (!timeout || isNaN(timeout)) timeout = this.#responseTimeout
|
|
|
|
const providerId = providerSlug.split('custom-')[1]
|
|
const provider = await Database.customMetadataProviderModel.findByPk(providerId)
|
|
|
|
if (!provider) {
|
|
throw new Error('Custom provider not found for the given id')
|
|
}
|
|
|
|
// Setup query params
|
|
const queryObj = {
|
|
mediaType,
|
|
query: title
|
|
}
|
|
if (author) {
|
|
queryObj.author = author
|
|
}
|
|
if (isbn) {
|
|
queryObj.isbn = isbn
|
|
}
|
|
const queryString = new URLSearchParams(queryObj).toString()
|
|
|
|
const url = `${provider.url}/search?${queryString}`
|
|
Logger.debug(`[CustomMetadataProvider] Search url: ${url}`)
|
|
|
|
// Setup headers
|
|
const axiosOptions = {
|
|
timeout
|
|
}
|
|
if (provider.authHeaderValue) {
|
|
axiosOptions.headers = {
|
|
Authorization: provider.authHeaderValue
|
|
}
|
|
}
|
|
|
|
const matches = await axios
|
|
.get(url, axiosOptions)
|
|
.then((res) => {
|
|
if (!res?.data || !Array.isArray(res.data.matches)) return null
|
|
return res.data.matches
|
|
})
|
|
.catch((error) => {
|
|
Logger.error('[CustomMetadataProvider] Search error', error)
|
|
return []
|
|
})
|
|
|
|
if (!matches) {
|
|
throw new Error('Custom provider returned malformed response')
|
|
}
|
|
|
|
const toStringOrUndefined = (value) => {
|
|
if (typeof value === 'string' || typeof value === 'number') return String(value)
|
|
if (Array.isArray(value) && value.every((v) => typeof v === 'string' || typeof v === 'number')) return value.join(',')
|
|
return undefined
|
|
}
|
|
const validateSeriesArray = (series) => {
|
|
if (!Array.isArray(series) || !series.length) return undefined
|
|
return series
|
|
.map((s) => {
|
|
if (!s?.series || typeof s.series !== 'string') return undefined
|
|
const _series = {
|
|
series: s.series
|
|
}
|
|
if (s.sequence && (typeof s.sequence === 'string' || typeof s.sequence === 'number')) {
|
|
_series.sequence = String(s.sequence)
|
|
}
|
|
return _series
|
|
})
|
|
.filter((s) => s !== undefined)
|
|
}
|
|
|
|
// re-map keys to throw out
|
|
return matches.map((match) => {
|
|
const { title, subtitle, author, narrator, publisher, publishedYear, description, cover, isbn, asin, genres, tags, series, language, duration } = match
|
|
|
|
const payload = {
|
|
title: toStringOrUndefined(title),
|
|
subtitle: toStringOrUndefined(subtitle),
|
|
author: toStringOrUndefined(author),
|
|
narrator: toStringOrUndefined(narrator),
|
|
publisher: toStringOrUndefined(publisher),
|
|
publishedYear: toStringOrUndefined(publishedYear),
|
|
description: description && typeof description === 'string' ? htmlSanitizer.sanitize(description) : undefined,
|
|
cover: toStringOrUndefined(cover),
|
|
isbn: toStringOrUndefined(isbn),
|
|
asin: toStringOrUndefined(asin),
|
|
genres: Array.isArray(genres) && genres.every((g) => typeof g === 'string') ? genres : undefined,
|
|
tags: toStringOrUndefined(tags),
|
|
series: validateSeriesArray(series),
|
|
language: toStringOrUndefined(language),
|
|
duration: !isNaN(duration) && duration !== null ? Number(duration) : undefined
|
|
}
|
|
|
|
// Remove undefined values
|
|
for (const key in payload) {
|
|
if (payload[key] === undefined) {
|
|
delete payload[key]
|
|
}
|
|
}
|
|
|
|
return payload
|
|
})
|
|
}
|
|
}
|
|
|
|
module.exports = CustomProviderAdapter
|