Compare commits

...

8 Commits

Author SHA1 Message Date
advplyr
0135b3560c Fix filesystem pathexists path join 2025-06-10 17:02:42 -05:00
advplyr
6968a5c02a Merge pull request #4378 from Vito0912/feat/PodcastNots
Notifications for failed rss feeds and disabled rss feeds
2025-06-09 16:25:19 -05:00
advplyr
5e2bb0b12c Fix notification js docs and update description/defaults 2025-06-09 16:21:05 -05:00
advplyr
7122756e58 Update notification description grammar 2025-06-09 15:51:14 -05:00
advplyr
8ecc912c2d Merge pull request #4388 from advplyr/book_author_secondary_sort
Update book library sort by author to use title as secondary sort #4380
2025-06-08 17:38:45 -05:00
Vito0912
f0525d4f0d abc is hard 2025-06-05 14:09:35 +02:00
Vito0912
346df3680c local strings 2025-06-05 14:02:29 +02:00
Vito0912
6aa7c8a3d8 added notification 2025-06-05 13:34:18 +02:00
5 changed files with 86 additions and 2 deletions

View File

@@ -918,6 +918,8 @@
"NotificationOnBackupCompletedDescription": "Triggered when a backup is completed",
"NotificationOnBackupFailedDescription": "Triggered when a backup fails",
"NotificationOnEpisodeDownloadedDescription": "Triggered when a podcast episode is auto-downloaded",
"NotificationOnRSSFeedDisabledDescription": "Triggered when automatic episode downloads are disabled due to too many failed attempts",
"NotificationOnRSSFeedFailedDescription": "Triggered when the RSS feed request fails for an automatic episode download",
"NotificationOnTestDescription": "Event for testing the notification system",
"PlaceholderNewCollection": "New collection name",
"PlaceholderNewFolderPath": "New folder path",

View File

@@ -89,7 +89,6 @@ class FileSystemController {
}
const { directory, folderPath } = req.body
if (!directory?.length || typeof directory !== 'string' || !folderPath?.length || typeof folderPath !== 'string') {
Logger.error(`[FileSystemController] Invalid request body: ${JSON.stringify(req.body)}`)
return res.status(400).json({
@@ -109,7 +108,8 @@ class FileSystemController {
return res.sendStatus(404)
}
const filepath = Path.posix.join(libraryFolder.path, directory)
const filepath = Path.join(libraryFolder.path, directory)
// Ensure filepath is inside library folder (prevents directory traversal)
if (!filepath.startsWith(libraryFolder.path)) {
Logger.error(`[FileSystemController] Filepath is not inside library folder: ${filepath}`)

View File

@@ -71,6 +71,54 @@ class NotificationManager {
this.triggerNotification('onBackupCompleted', eventData)
}
/**
* Handles scheduled episode download RSS feed request failed
*
* @param {string} feedUrl
* @param {number} numFailed
* @param {string} title
*/
async onRSSFeedFailed(feedUrl, numFailed, title) {
if (!Database.notificationSettings.isUseable) return
if (!Database.notificationSettings.getHasActiveNotificationsForEvent('onRSSFeedFailed')) {
Logger.debug(`[NotificationManager] onRSSFeedFailed: No active notifications`)
return
}
Logger.debug(`[NotificationManager] onRSSFeedFailed: RSS feed request failed for ${feedUrl}`)
const eventData = {
feedUrl: feedUrl,
numFailed: numFailed || 0,
title: title || 'Unknown Title'
}
this.triggerNotification('onRSSFeedFailed', eventData)
}
/**
* Handles scheduled episode downloads disabled due to too many failed attempts
*
* @param {string} feedUrl
* @param {number} numFailed
* @param {string} title
*/
async onRSSFeedDisabled(feedUrl, numFailed, title) {
if (!Database.notificationSettings.isUseable) return
if (!Database.notificationSettings.getHasActiveNotificationsForEvent('onRSSFeedDisabled')) {
Logger.debug(`[NotificationManager] onRSSFeedDisabled: No active notifications`)
return
}
Logger.debug(`[NotificationManager] onRSSFeedDisabled: Podcast scheduled episode download disabled due to ${numFailed} failed requests for ${feedUrl}`)
const eventData = {
feedUrl: feedUrl,
numFailed: numFailed || 0,
title: title || 'Unknown Title'
}
this.triggerNotification('onRSSFeedDisabled', eventData)
}
/**
*
* @param {string} errorMsg

View File

@@ -347,10 +347,12 @@ class PodcastManager {
this.failedCheckMap[libraryItem.id]++
if (this.MaxFailedEpisodeChecks !== 0 && this.failedCheckMap[libraryItem.id] >= this.MaxFailedEpisodeChecks) {
Logger.error(`[PodcastManager] runEpisodeCheck ${this.failedCheckMap[libraryItem.id]} failed attempts at checking episodes for "${libraryItem.media.title}" - disabling auto download`)
void NotificationManager.onRSSFeedDisabled(libraryItem.media.feedURL, this.failedCheckMap[libraryItem.id], libraryItem.media.title)
libraryItem.media.autoDownloadEpisodes = false
delete this.failedCheckMap[libraryItem.id]
} else {
Logger.warn(`[PodcastManager] runEpisodeCheck ${this.failedCheckMap[libraryItem.id]} failed attempts at checking episodes for "${libraryItem.media.title}"`)
void NotificationManager.onRSSFeedFailed(libraryItem.media.feedURL, this.failedCheckMap[libraryItem.id], libraryItem.media.title)
}
} else if (newEpisodes.length) {
delete this.failedCheckMap[libraryItem.id]

View File

@@ -60,6 +60,38 @@ module.exports.notificationData = {
errorMsg: 'Example error message'
}
},
{
name: 'onRSSFeedFailed',
requiresLibrary: true,
description: 'Triggered when the RSS feed request fails for an automatic episode download',
descriptionKey: 'NotificationOnRSSFeedFailedDescription',
variables: ['feedUrl', 'numFailed', 'title'],
defaults: {
title: 'RSS Feed Request Failed',
body: 'Failed to request RSS feed for {{title}}.\nFeed URL: {{feedUrl}}\nNumber of failed attempts: {{numFailed}}'
},
testData: {
title: 'Test RSS Feed',
feedUrl: 'https://example.com/rss',
numFailed: 3
}
},
{
name: 'onRSSFeedDisabled',
requiresLibrary: true,
description: 'Triggered when automatic episode downloads are disabled due to too many failed attempts',
descriptionKey: 'NotificationOnRSSFeedDisabledDescription',
variables: ['feedUrl', 'numFailed', 'title'],
defaults: {
title: 'Podcast Episode Download Schedule Disabled',
body: 'Automatic episode downloads for {{title}} have been disabled due to too many failed RSS feed requests.\nFeed URL: {{feedUrl}}\nNumber of failed attempts: {{numFailed}}'
},
testData: {
title: 'Test RSS Feed',
feedUrl: 'https://example.com/rss',
numFailed: 5
}
},
{
name: 'onTest',
requiresLibrary: false,