Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: HTTP/429 when requesting authors information, resolves #1570 #2188

Merged
merged 11 commits into from
Jun 9, 2024
1,681 changes: 773 additions & 908 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"node-tone": "^1.0.1",
"nodemailer": "^6.9.13",
"openid-client": "^5.6.1",
"p-throttle": "^4.1.1",
"passport": "^0.6.0",
"passport-jwt": "^4.0.1",
"sequelize": "^6.35.2",
Expand Down
4 changes: 2 additions & 2 deletions server/controllers/AuthorController.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
56 changes: 31 additions & 25 deletions server/controllers/PodcastController.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ const CoverManager = require('../managers/CoverManager')
const LibraryItem = require('../objects/LibraryItem')

class PodcastController {

async create(req, res) {
if (!req.user.isAdminOrUp) {
Logger.error(`[PodcastController] Non-admin user "${req.user.username}" attempted to create podcast`)
Expand All @@ -28,7 +27,7 @@ class PodcastController {
return res.status(404).send('Library not found')
}

const folder = library.folders.find(fold => fold.id === payload.folderId)
const folder = library.folders.find((fold) => fold.id === payload.folderId)
if (!folder) {
Logger.error(`[PodcastController] Create: Folder not found "${payload.folderId}"`)
return res.status(404).send('Folder not found')
Expand All @@ -37,20 +36,24 @@ class PodcastController {
const podcastPath = filePathToPOSIX(payload.path)

// Check if a library item with this podcast folder exists already
const existingLibraryItem = (await Database.libraryItemModel.count({
where: {
path: podcastPath
}
})) > 0
const existingLibraryItem =
(await Database.libraryItemModel.count({
where: {
path: podcastPath
}
})) > 0
if (existingLibraryItem) {
Logger.error(`[PodcastController] Podcast already exists at path "${podcastPath}"`)
return res.status(400).send('Podcast already exists')
}

const success = await fs.ensureDir(podcastPath).then(() => true).catch((error) => {
Logger.error(`[PodcastController] Failed to ensure podcast dir "${podcastPath}"`, error)
return false
})
const success = await fs
.ensureDir(podcastPath)
.then(() => true)
.catch((error) => {
Logger.error(`[PodcastController] Failed to ensure podcast dir "${podcastPath}"`, error)
return false
})
if (!success) return res.status(400).send('Invalid podcast path')

const libraryItemFolderStats = await getFileTimestampsWithIno(podcastPath)
Expand Down Expand Up @@ -105,12 +108,12 @@ class PodcastController {

/**
* POST: /api/podcasts/feed
*
*
* @typedef getPodcastFeedReqBody
* @property {string} rssFeed
*
* @param {import('express').Request<{}, {}, getPodcastFeedReqBody, {}} req
* @param {import('express').Response} res
*
* @param {import('express').Request<{}, {}, getPodcastFeedReqBody, {}} req
* @param {import('express').Response} res
*/
async getPodcastFeed(req, res) {
if (!req.user.isAdminOrUp) {
Expand Down Expand Up @@ -178,7 +181,7 @@ class PodcastController {

var downloadsInQueue = this.podcastManager.getEpisodeDownloadsInQueue(libraryItem.id)
res.json({
downloads: downloadsInQueue.map(d => d.toJSONForClient())
downloads: downloadsInQueue.map((d) => d.toJSONForClient())
})
}

Expand All @@ -189,8 +192,8 @@ class PodcastController {
return res.status(500).send('Podcast does not have an RSS feed URL')
}

var searchTitle = req.query.title
if (!searchTitle) {
const searchTitle = req.query.title
if (!searchTitle || typeof searchTitle !== 'string') {
return res.sendStatus(500)
}
const episodes = await findMatchingEpisodes(rssFeedUrl, searchTitle)
Expand Down Expand Up @@ -254,7 +257,7 @@ class PodcastController {
const episodeId = req.params.episodeId
const libraryItem = req.libraryItem

const episode = libraryItem.media.episodes.find(ep => ep.id === episodeId)
const episode = libraryItem.media.episodes.find((ep) => ep.id === episodeId)
if (!episode) {
Logger.error(`[PodcastController] getEpisode episode ${episodeId} not found for item ${libraryItem.id}`)
return res.sendStatus(404)
Expand All @@ -269,7 +272,7 @@ class PodcastController {
const libraryItem = req.libraryItem
const hardDelete = req.query.hard === '1'

const episode = libraryItem.media.episodes.find(ep => ep.id === episodeId)
const episode = libraryItem.media.episodes.find((ep) => ep.id === episodeId)
if (!episode) {
Logger.error(`[PodcastController] removeEpisode episode ${episodeId} not found for item ${libraryItem.id}`)
return res.sendStatus(404)
Expand All @@ -278,11 +281,14 @@ class PodcastController {
if (hardDelete) {
const audioFile = episode.audioFile
// TODO: this will trigger the watcher. should maybe handle this gracefully
await fs.remove(audioFile.metadata.path).then(() => {
Logger.info(`[PodcastController] Hard deleted episode file at "${audioFile.metadata.path}"`)
}).catch((error) => {
Logger.error(`[PodcastController] Failed to hard delete episode file at "${audioFile.metadata.path}"`, error)
})
await fs
.remove(audioFile.metadata.path)
.then(() => {
Logger.info(`[PodcastController] Hard deleted episode file at "${audioFile.metadata.path}"`)
})
.catch((error) => {
Logger.error(`[PodcastController] Failed to hard delete episode file at "${audioFile.metadata.path}"`, error)
})
}

// Remove episode from Podcast and library file
Expand Down
18 changes: 11 additions & 7 deletions server/controllers/SearchController.js
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand All @@ -78,4 +82,4 @@ class SearchController {
})
}
}
module.exports = new SearchController()
module.exports = new SearchController()
15 changes: 3 additions & 12 deletions server/providers/Audible.js
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)]
}

Expand Down
Loading
Loading