Add:API endpoint for opening RSS feed for collection #606 #1265

This commit is contained in:
advplyr 2022-12-26 17:48:39 -06:00
parent e803dcd325
commit 061695f922
6 changed files with 157 additions and 7 deletions

View file

@ -1,4 +1,3 @@
const Logger = require('../Logger')
const { getId } = require('../utils/index')
class Collection {
@ -46,6 +45,18 @@ class Collection {
return json
}
// Expanded and filtered out items not accessible to user
toJSONExpandedForUser(user, libraryItems) {
const json = this.toJSON()
json.books = json.books.map(libraryItemId => {
const libraryItem = libraryItems.find(li => li.id === libraryItemId)
return libraryItem ? libraryItem.toJSONExpanded() : null
}).filter(li => {
return li && user.checkCanAccessLibraryItem(li)
})
return json
}
construct(collection) {
this.id = collection.id
this.libraryId = collection.libraryId

View file

@ -129,6 +129,7 @@ class Feed {
const author = isPodcast ? mediaMetadata.author : mediaMetadata.authorName
this.entityUpdatedAt = libraryItem.updatedAt
this.coverPath = media.coverPath || null
this.meta.title = mediaMetadata.title
this.meta.description = mediaMetadata.description
@ -155,6 +156,72 @@ class Feed {
this.xml = null
}
setFromCollection(userId, slug, collectionExpanded, serverAddress) {
const feedUrl = `${serverAddress}/feed/${slug}`
const itemsWithTracks = collectionExpanded.books.filter(libraryItem => libraryItem.media.tracks.length)
const firstItemWithCover = itemsWithTracks.find(item => item.media.coverPath)
this.id = slug
this.slug = slug
this.userId = userId
this.entityType = 'collection'
this.entityId = collectionExpanded.id
this.entityUpdatedAt = collectionExpanded.lastUpdate
this.coverPath = firstItemWithCover?.coverPath || null
this.serverAddress = serverAddress
this.feedUrl = feedUrl
this.meta = new FeedMeta()
this.meta.title = collectionExpanded.name
this.meta.description = collectionExpanded.description || ''
this.meta.author = this.getAuthorsStringFromLibraryItems(itemsWithTracks)
this.meta.imageUrl = this.coverPath ? `${serverAddress}/feed/${slug}/cover` : `${serverAddress}/Logo.png`
this.meta.feedUrl = feedUrl
this.meta.link = `${serverAddress}/collection/${collectionExpanded.id}`
this.meta.explicit = !!itemsWithTracks.some(li => li.media.metadata.explicit) // explicit if any item is explicit
this.episodes = []
itemsWithTracks.forEach((item, index) => {
item.media.tracks.forEach((audioTrack) => {
const feedEpisode = new FeedEpisode()
feedEpisode.setFromAudiobookTrack(item, serverAddress, slug, audioTrack, this.meta, index)
this.episodes.push(feedEpisode)
})
})
this.createdAt = Date.now()
this.updatedAt = Date.now()
}
updateFromCollection(collectionExpanded) {
const itemsWithTracks = collectionExpanded.books.filter(libraryItem => libraryItem.media.tracks.length)
const firstItemWithCover = itemsWithTracks.find(item => item.media.coverPath)
this.entityUpdatedAt = collectionExpanded.lastUpdate
this.coverPath = firstItemWithCover?.coverPath || null
this.meta.title = collectionExpanded.name
this.meta.description = collectionExpanded.description || ''
this.meta.author = this.getAuthorsStringFromLibraryItems(itemsWithTracks)
this.meta.imageUrl = this.coverPath ? `${this.serverAddress}/feed/${this.slug}/cover` : `${this.serverAddress}/Logo.png`
this.meta.explicit = !!itemsWithTracks.some(li => li.media.metadata.explicit) // explicit if any item is explicit
this.episodes = []
itemsWithTracks.forEach((item, index) => {
item.media.tracks.forEach((audioTrack) => {
const feedEpisode = new FeedEpisode()
feedEpisode.setFromAudiobookTrack(item, this.serverAddress, this.slug, audioTrack, this.meta, index)
this.episodes.push(feedEpisode)
})
})
this.updatedAt = Date.now()
this.xml = null
}
buildXml() {
if (this.xml) return this.xml
@ -165,5 +232,16 @@ class Feed {
this.xml = rssfeed.xml()
return this.xml
}
getAuthorsStringFromLibraryItems(libraryItems) {
let itemAuthors = []
libraryItems.forEach((item) => itemAuthors.push(...item.media.metadata.authors.map(au => au.name)))
itemAuthors = [...new Set(itemAuthors)] // Filter out dupes
let author = itemAuthors.slice(0, 3).join(', ')
if (itemAuthors.length > 3) {
author += ' & more'
}
return author
}
}
module.exports = Feed

View file

@ -83,9 +83,15 @@ class FeedEpisode {
this.fullPath = episode.audioFile.metadata.path
}
setFromAudiobookTrack(libraryItem, serverAddress, slug, audioTrack, meta) {
setFromAudiobookTrack(libraryItem, serverAddress, slug, audioTrack, meta, additionalOffset = 0) {
// Example: <pubDate>Fri, 04 Feb 2015 00:00:00 GMT</pubDate>
const timeOffset = isNaN(audioTrack.index) ? 0 : (Number(audioTrack.index) * 1000) // Offset pubdate to ensure correct order
let timeOffset = isNaN(audioTrack.index) ? 0 : (Number(audioTrack.index) * 1000) // Offset pubdate to ensure correct order
// Additional offset can be used for collections/series
if (additionalOffset && !isNaN(additionalOffset)) {
timeOffset += Number(additionalOffset) * 1000
}
// e.g. Track 1 will have a pub date before Track 2
const audiobookPubDate = date.format(new Date(libraryItem.addedAt + timeOffset), 'ddd, DD MMM YYYY HH:mm:ss [GMT]')