From aad2d9a017c10b16ba29312dbbfb48ffa65c5f14 Mon Sep 17 00:00:00 2001 From: matt Date: Fri, 27 Sep 2024 10:54:41 -0500 Subject: [PATCH 1/2] Added notification support for new items being added to a library. Supports the client upload method (one or more items), or folder changes. --- docs/objects/Notification.yaml | 2 +- docs/openapi.json | 1 + server/managers/NotificationManager.js | 26 ++++++++++++++++++++++++++ server/scanner/LibraryScanner.js | 8 ++++++-- server/utils/notifications.js | 22 ++++++++++++++++++++++ 5 files changed, 56 insertions(+), 3 deletions(-) diff --git a/docs/objects/Notification.yaml b/docs/objects/Notification.yaml index 50299ec8be..05c7d93764 100644 --- a/docs/objects/Notification.yaml +++ b/docs/objects/Notification.yaml @@ -22,7 +22,7 @@ components: notificationEventName: type: string description: The name of the event the notification will fire on. - enum: ['onPodcastEpisodeDownloaded', 'onBackupCompleted', 'onBackupFailed', 'onTest'] + enum: ['onItemsAdded', 'onPodcastEpisodeDownloaded', 'onBackupCompleted', 'onBackupFailed', 'onTest'] urls: type: array items: diff --git a/docs/openapi.json b/docs/openapi.json index 48f30ecfb0..bab29e157d 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -3225,6 +3225,7 @@ "type": "string", "description": "The name of the event the notification will fire on.", "enum": [ + "onItemsAdded", "onPodcastEpisodeDownloaded", "onBackupCompleted", "onBackupFailed", diff --git a/server/managers/NotificationManager.js b/server/managers/NotificationManager.js index a59c128154..dffc642f39 100644 --- a/server/managers/NotificationManager.js +++ b/server/managers/NotificationManager.js @@ -14,6 +14,32 @@ class NotificationManager { return notificationData } + async onItemsAdded(LibraryItems) { + if (!Database.notificationSettings.isUseable) return + + if (!Database.notificationSettings.getHasActiveNotificationsForEvent('onItemsAdded')) { + Logger.debug(`[NotificationManager] onItemsAdded: No active notifications`) + return + } + + for (const item of LibraryItems) { + Logger.debug(`[NotificationManager] onItemsAdded: Item "${item.media.metadata.title}"`) + const library = await Database.libraryModel.findByPk(item.libraryId) + const eventData = { + libraryItemId: item.id, + libraryId: item.libraryId, + libraryName: library?.name || 'Unknown', + tags: (item.media.tags || []).join(', ') || 'None', + title: item.media.metadata.title, + authors: (item.media.metadata.authors.map(a => a.name) || []).join(', ') || '', + description: item.media.metadata.description || '', + genres: (item.media.metadata.genres || []).join(', ') || 'None', + publishedYear: item.media.metadata.publishedYear || '', + } + this.triggerNotification('onItemsAdded', eventData) + } + } + async onPodcastEpisodeDownloaded(libraryItem, episode) { if (!Database.notificationSettings.isUseable) return diff --git a/server/scanner/LibraryScanner.js b/server/scanner/LibraryScanner.js index bd0bb310f5..02e261f399 100644 --- a/server/scanner/LibraryScanner.js +++ b/server/scanner/LibraryScanner.js @@ -16,7 +16,8 @@ const LibraryItemScanData = require('./LibraryItemScanData') const Task = require('../objects/Task') class LibraryScanner { - constructor() { + constructor(notificationManager) { + this.notificationManager = notificationManager this.cancelLibraryScan = {} /** @type {string[]} - library ids */ this.librariesScanning = [] @@ -284,6 +285,7 @@ class LibraryScanner { 'items_added', newOldLibraryItems.map((li) => li.toJSONExpanded()) ) + this.notificationManager.onItemsAdded(newOldLibraryItems) newOldLibraryItems = [] } @@ -296,6 +298,7 @@ class LibraryScanner { 'items_added', newOldLibraryItems.map((li) => li.toJSONExpanded()) ) + this.notificationManager.onItemsAdded(newOldLibraryItems) } } @@ -647,6 +650,7 @@ class LibraryScanner { if (newLibraryItem) { const oldNewLibraryItem = Database.libraryItemModel.getOldLibraryItem(newLibraryItem) SocketAuthority.emitter('item_added', oldNewLibraryItem.toJSONExpanded()) + this.notificationManager.onItemsAdded([oldNewLibraryItem]) } itemGroupingResults[itemDir] = newLibraryItem ? ScanResult.ADDED : ScanResult.NOTHING } @@ -654,7 +658,7 @@ class LibraryScanner { return itemGroupingResults } } -module.exports = new LibraryScanner() +module.exports = new LibraryScanner(new NotificationManager()) function ItemToFileInoMatch(libraryItem1, libraryItem2) { return libraryItem1.isFile && libraryItem2.libraryFiles.some((lf) => lf.ino === libraryItem1.ino) diff --git a/server/utils/notifications.js b/server/utils/notifications.js index 96e8ddf8cd..a8e0e58661 100644 --- a/server/utils/notifications.js +++ b/server/utils/notifications.js @@ -2,6 +2,28 @@ const { version } = require('../../package.json') module.exports.notificationData = { events: [ + { + name: 'onItemsAdded', + requiresLibrary: true, + libraryMediaType: 'item', + description: 'Triggered when an item is added to the library', + variables: ['libraryItemId', 'libraryId', 'libraryName', 'tags', 'title', 'authors', 'description', 'genres', 'publishedYear'], + defaults: { + title: 'New Book!', + body: '{{title}} has been added to {{libraryName}} library.' + }, + testData: { + libraryItemId: 'li_notification_test', + libraryId: 'lib_test', + libraryName: 'My Library', + tags: 'TestTag1, TestTag2', + title: 'ABS Test Book', + authors: 'Author1, Author2', + description: 'Description of the Abs Test Book belongs here.', + genres: 'TestGenre1, TestGenre2', + publishedYear: '2020' + } + }, { name: 'onPodcastEpisodeDownloaded', requiresLibrary: true, From 9513580043045d028a83b985ebc49f79da5e30dc Mon Sep 17 00:00:00 2001 From: matt Date: Fri, 27 Sep 2024 20:23:58 -0500 Subject: [PATCH 2/2] Updated to use new NotificationManager singleton. --- server/scanner/LibraryScanner.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/server/scanner/LibraryScanner.js b/server/scanner/LibraryScanner.js index 02e261f399..78ec422f27 100644 --- a/server/scanner/LibraryScanner.js +++ b/server/scanner/LibraryScanner.js @@ -14,10 +14,10 @@ const LibraryItemScanner = require('./LibraryItemScanner') const LibraryScan = require('./LibraryScan') const LibraryItemScanData = require('./LibraryItemScanData') const Task = require('../objects/Task') +const NotificationManager = require('../managers/NotificationManager') class LibraryScanner { - constructor(notificationManager) { - this.notificationManager = notificationManager + constructor() { this.cancelLibraryScan = {} /** @type {string[]} - library ids */ this.librariesScanning = [] @@ -285,7 +285,7 @@ class LibraryScanner { 'items_added', newOldLibraryItems.map((li) => li.toJSONExpanded()) ) - this.notificationManager.onItemsAdded(newOldLibraryItems) + NotificationManager.onItemsAdded(newOldLibraryItems) newOldLibraryItems = [] } @@ -298,7 +298,7 @@ class LibraryScanner { 'items_added', newOldLibraryItems.map((li) => li.toJSONExpanded()) ) - this.notificationManager.onItemsAdded(newOldLibraryItems) + NotificationManager.onItemsAdded(newOldLibraryItems) } } @@ -650,7 +650,7 @@ class LibraryScanner { if (newLibraryItem) { const oldNewLibraryItem = Database.libraryItemModel.getOldLibraryItem(newLibraryItem) SocketAuthority.emitter('item_added', oldNewLibraryItem.toJSONExpanded()) - this.notificationManager.onItemsAdded([oldNewLibraryItem]) + NotificationManager.onItemsAdded([oldNewLibraryItem]) } itemGroupingResults[itemDir] = newLibraryItem ? ScanResult.ADDED : ScanResult.NOTHING } @@ -658,7 +658,7 @@ class LibraryScanner { return itemGroupingResults } } -module.exports = new LibraryScanner(new NotificationManager()) +module.exports = new LibraryScanner() function ItemToFileInoMatch(libraryItem1, libraryItem2) { return libraryItem1.isFile && libraryItem2.libraryFiles.some((lf) => lf.ino === libraryItem1.ino)