Skip to content

Commit

Permalink
Migrate Feed updating and build xml to new model
Browse files Browse the repository at this point in the history
  • Loading branch information
advplyr committed Dec 15, 2024
1 parent 369c059 commit f8fbd3a
Show file tree
Hide file tree
Showing 10 changed files with 370 additions and 423 deletions.
9 changes: 8 additions & 1 deletion server/controllers/CollectionController.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ class CollectionController {
}

// If books array is passed in then update order in collection
let collectionBooksUpdated = false
if (req.body.books?.length) {
const collectionBooks = await req.collection.getCollectionBooks({
include: {
Expand All @@ -134,9 +135,15 @@ class CollectionController {
await collectionBooks[i].update({
order: i + 1
})
wasUpdated = true
collectionBooksUpdated = true
}
}

if (collectionBooksUpdated) {
req.collection.changed('updatedAt', true)
await req.collection.save()
wasUpdated = true
}
}

const jsonExpanded = await req.collection.getOldJsonExpanded()
Expand Down
12 changes: 4 additions & 8 deletions server/controllers/RSSFeedController.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,6 @@ class RSSFeedController {
async openRSSFeedForCollection(req, res) {
const reqBody = req.body || {}

const collection = await Database.collectionModel.findByPk(req.params.collectionId)
if (!collection) return res.sendStatus(404)

// Check request body options exist
if (!reqBody.serverAddress || !reqBody.slug || typeof reqBody.serverAddress !== 'string' || typeof reqBody.slug !== 'string') {
Logger.error(`[RSSFeedController] Invalid request body to open RSS feed`)
Expand All @@ -105,7 +102,8 @@ class RSSFeedController {
return res.status(400).send('Slug already in use')
}

collection.books = await collection.getBooksExpandedWithLibraryItem()
const collection = await Database.collectionModel.getExpandedById(req.params.collectionId)
if (!collection) return res.sendStatus(404)

// Check collection has audio tracks
if (!collection.books.some((book) => book.includedAudioFiles.length)) {
Expand Down Expand Up @@ -135,9 +133,6 @@ class RSSFeedController {
async openRSSFeedForSeries(req, res) {
const reqBody = req.body || {}

const series = await Database.seriesModel.findByPk(req.params.seriesId)
if (!series) return res.sendStatus(404)

// Check request body options exist
if (!reqBody.serverAddress || !reqBody.slug || typeof reqBody.serverAddress !== 'string' || typeof reqBody.slug !== 'string') {
Logger.error(`[RSSFeedController] Invalid request body to open RSS feed`)
Expand All @@ -150,7 +145,8 @@ class RSSFeedController {
return res.status(400).send('Slug already in use')
}

series.books = await series.getBooksExpandedWithLibraryItem()
const series = await Database.seriesModel.getExpandedById(req.params.seriesId)
if (!series) return res.sendStatus(404)

// Check series has audio tracks
if (!series.books.some((book) => book.includedAudioFiles.length)) {
Expand Down
145 changes: 71 additions & 74 deletions server/managers/RssFeedManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ const SocketAuthority = require('../SocketAuthority')
const Database = require('../Database')

const fs = require('../libs/fsExtra')
const libraryItemsBookFilters = require('../utils/queries/libraryItemsBookFilters')

class RssFeedManager {
constructor() {}
Expand Down Expand Up @@ -70,94 +69,92 @@ class RssFeedManager {
}

/**
* GET: /feed/:slug
* Feed requires update if the entity (or child entities) has been updated since the feed was last updated
*
* @param {Request} req
* @param {Response} res
* @param {import('../models/Feed')} feed
* @returns {Promise<boolean>}
*/
async getFeed(req, res) {
const feed = await this.findFeedBySlug(req.params.slug)
if (!feed) {
Logger.warn(`[RssFeedManager] Feed not found ${req.params.slug}`)
res.sendStatus(404)
return
}

// Check if feed needs to be updated
async checkFeedRequiresUpdate(feed) {
if (feed.entityType === 'libraryItem') {
const libraryItem = await Database.libraryItemModel.getOldById(feed.entityId)
feed.entity = await feed.getEntity({
attributes: ['id', 'updatedAt', 'mediaId', 'mediaType']
})

let newEntityUpdatedAt = feed.entity.updatedAt

let mostRecentlyUpdatedAt = libraryItem.updatedAt
if (libraryItem.isPodcast) {
libraryItem.media.episodes.forEach((episode) => {
if (episode.updatedAt > mostRecentlyUpdatedAt) mostRecentlyUpdatedAt = episode.updatedAt
if (feed.entity.mediaType === 'podcast') {
const mostRecentPodcastEpisode = await Database.podcastEpisodeModel.findOne({
where: {
podcastId: feed.entity.mediaId
},
attributes: ['id', 'updatedAt'],
order: [['createdAt', 'DESC']]
})
if (mostRecentPodcastEpisode && mostRecentPodcastEpisode.updatedAt > newEntityUpdatedAt) {
newEntityUpdatedAt = mostRecentPodcastEpisode.updatedAt
}
}

if (libraryItem && (!feed.entityUpdatedAt || mostRecentlyUpdatedAt > feed.entityUpdatedAt)) {
Logger.debug(`[RssFeedManager] Updating RSS feed for item ${libraryItem.id} "${libraryItem.media.metadata.title}"`)

feed.updateFromItem(libraryItem)
await Database.updateFeed(feed)
}
} else if (feed.entityType === 'collection') {
const collection = await Database.collectionModel.findByPk(feed.entityId, {
include: Database.collectionBookModel
})
if (collection) {
const collectionExpanded = await collection.getOldJsonExpanded()

// Find most recently updated item in collection
let mostRecentlyUpdatedAt = collectionExpanded.lastUpdate
// Check for most recently updated book
collectionExpanded.books.forEach((libraryItem) => {
if (libraryItem.media.tracks.length && libraryItem.updatedAt > mostRecentlyUpdatedAt) {
mostRecentlyUpdatedAt = libraryItem.updatedAt
return newEntityUpdatedAt > feed.entityUpdatedAt
} else if (feed.entityType === 'collection' || feed.entityType === 'series') {
feed.entity = await feed.getEntity({
attributes: ['id', 'updatedAt'],
include: {
model: Database.bookModel,
attributes: ['id'],
through: {
attributes: []
},
include: {
model: Database.libraryItemModel,
attributes: ['id', 'updatedAt']
}
})
// Check for most recently added collection book
collection.collectionBooks.forEach((collectionBook) => {
if (collectionBook.createdAt.valueOf() > mostRecentlyUpdatedAt) {
mostRecentlyUpdatedAt = collectionBook.createdAt.valueOf()
}
})
const hasBooksRemoved = collection.collectionBooks.length < feed.episodes.length
}
})

if (!feed.entityUpdatedAt || hasBooksRemoved || mostRecentlyUpdatedAt > feed.entityUpdatedAt) {
Logger.debug(`[RssFeedManager] Updating RSS feed for collection "${collection.name}"`)
let newEntityUpdatedAt = feed.entity.updatedAt

feed.updateFromCollection(collectionExpanded)
await Database.updateFeed(feed)
const mostRecentItemUpdatedAt = feed.entity.books.reduce((mostRecent, book) => {
if (book.libraryItem.updatedAt > mostRecent) {
return book.libraryItem.updatedAt
}
return mostRecent
}, 0)

if (mostRecentItemUpdatedAt > newEntityUpdatedAt) {
newEntityUpdatedAt = mostRecentItemUpdatedAt
}
} else if (feed.entityType === 'series') {
const series = await Database.seriesModel.findByPk(feed.entityId)
if (series) {
const seriesJson = series.toOldJSON()

// Get books in series that have audio tracks
seriesJson.books = (await libraryItemsBookFilters.getLibraryItemsForSeries(series)).filter((li) => li.media.numTracks)

// Find most recently updated item in series
let mostRecentlyUpdatedAt = seriesJson.updatedAt
let totalTracks = 0 // Used to detect series items removed
seriesJson.books.forEach((libraryItem) => {
totalTracks += libraryItem.media.tracks.length
if (libraryItem.media.tracks.length && libraryItem.updatedAt > mostRecentlyUpdatedAt) {
mostRecentlyUpdatedAt = libraryItem.updatedAt
}
})
if (totalTracks !== feed.episodes.length) {
mostRecentlyUpdatedAt = Date.now()
}

if (!feed.entityUpdatedAt || mostRecentlyUpdatedAt > feed.entityUpdatedAt) {
Logger.debug(`[RssFeedManager] Updating RSS feed for series "${seriesJson.name}"`)
return newEntityUpdatedAt > feed.entityUpdatedAt
} else {
throw new Error('Invalid feed entity type')
}
}

feed.updateFromSeries(seriesJson)
await Database.updateFeed(feed)
}
/**
* GET: /feed/:slug
*
* @param {Request} req
* @param {Response} res
*/
async getFeed(req, res) {
let feed = await Database.feedModel.findOne({
where: {
slug: req.params.slug
}
})
if (!feed) {
Logger.warn(`[RssFeedManager] Feed not found ${req.params.slug}`)
res.sendStatus(404)
return
}

const feedRequiresUpdate = await this.checkFeedRequiresUpdate(feed)
if (feedRequiresUpdate) {
Logger.info(`[RssFeedManager] Feed "${feed.title}" requires update - updating feed`)
feed = await feed.updateFeedForEntity()
} else {
feed.feedEpisodes = await feed.getFeedEpisodes()
}

const xml = feed.buildXml(req.originalHostPrefix)
Expand Down
34 changes: 33 additions & 1 deletion server/models/Collection.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
const { DataTypes, Model, Sequelize } = require('sequelize')

const oldCollection = require('../objects/Collection')
const Logger = require('../Logger')

class Collection extends Model {
constructor(values, options) {
Expand Down Expand Up @@ -121,6 +120,39 @@ class Collection extends Model {
.filter((c) => c)
}

/**
*
* @param {string} collectionId
* @returns {Promise<Collection>}
*/
static async getExpandedById(collectionId) {
return this.findByPk(collectionId, {
include: [
{
model: this.sequelize.models.book,
include: [
{
model: this.sequelize.models.libraryItem
},
{
model: this.sequelize.models.author,
through: {
attributes: []
}
},
{
model: this.sequelize.models.series,
through: {
attributes: ['sequence']
}
}
]
}
],
order: [[this.sequelize.models.book, this.sequelize.models.collectionBook, 'order', 'ASC']]
})
}

/**
* Get old collection from Collection
* @param {Collection} collectionExpanded
Expand Down
Loading

0 comments on commit f8fbd3a

Please sign in to comment.