mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-08-02 01:05:25 +02:00
Add:Persist RSS feeds in db #696, Update:RSS feed data model
This commit is contained in:
parent
881baa818d
commit
fbbceaa642
9 changed files with 366 additions and 130 deletions
|
@ -2,6 +2,7 @@ const Path = require('path')
|
|||
const fs = require('fs-extra')
|
||||
const date = require('date-and-time')
|
||||
const { Podcast } = require('podcast')
|
||||
const Feed = require('../objects/Feed')
|
||||
const Logger = require('../Logger')
|
||||
|
||||
// Not functional at the moment
|
||||
|
@ -12,137 +13,73 @@ class RssFeedManager {
|
|||
this.feeds = {}
|
||||
}
|
||||
|
||||
async init() {
|
||||
var feedObjects = await this.db.getAllEntities('feed')
|
||||
if (feedObjects && feedObjects.length) {
|
||||
feedObjects.forEach((feedObj) => {
|
||||
var feed = new Feed(feedObj)
|
||||
this.feeds[feed.id] = feed
|
||||
Logger.info(`[RssFeedManager] Opened rss feed ${feed.feedUrl}`)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
findFeedForItem(libraryItemId) {
|
||||
return Object.values(this.feeds).find(feed => feed.libraryItemId === libraryItemId)
|
||||
return Object.values(this.feeds).find(feed => feed.entityId === libraryItemId)
|
||||
}
|
||||
|
||||
getFeed(req, res) {
|
||||
var feedData = this.feeds[req.params.id]
|
||||
if (!feedData) {
|
||||
var feed = this.feeds[req.params.id]
|
||||
if (!feed) {
|
||||
Logger.error(`[RssFeedManager] Feed not found ${req.params.id}`)
|
||||
res.sendStatus(404)
|
||||
return
|
||||
}
|
||||
var xml = feedData.feed.buildXml()
|
||||
// var xml = feedData.feed.buildXml()
|
||||
var xml = feed.buildXml()
|
||||
res.set('Content-Type', 'text/xml')
|
||||
res.send(xml)
|
||||
}
|
||||
|
||||
getFeedItem(req, res) {
|
||||
var feedData = this.feeds[req.params.id]
|
||||
if (!feedData) {
|
||||
var feed = this.feeds[req.params.id]
|
||||
if (!feed) {
|
||||
Logger.error(`[RssFeedManager] Feed not found ${req.params.id}`)
|
||||
res.sendStatus(404)
|
||||
return
|
||||
}
|
||||
var remainingPath = req.params['0']
|
||||
var fullPath = Path.join(feedData.libraryItemPath, remainingPath)
|
||||
res.sendFile(fullPath)
|
||||
var episodePath = feed.getEpisodePath(req.params.episodeId)
|
||||
if (!episodePath) {
|
||||
Logger.error(`[RssFeedManager] Feed episode not found ${req.params.episodeId}`)
|
||||
res.sendStatus(404)
|
||||
return
|
||||
}
|
||||
res.sendFile(episodePath)
|
||||
// var remainingPath = req.params['0']
|
||||
// var fullPath = Path.join(feedData.libraryItemPath, remainingPath)
|
||||
// res.sendFile(fullPath)
|
||||
}
|
||||
|
||||
getFeedCover(req, res) {
|
||||
var feedData = this.feeds[req.params.id]
|
||||
if (!feedData) {
|
||||
var feed = this.feeds[req.params.id]
|
||||
if (!feed) {
|
||||
Logger.error(`[RssFeedManager] Feed not found ${req.params.id}`)
|
||||
res.sendStatus(404)
|
||||
return
|
||||
}
|
||||
|
||||
if (!feedData.mediaCoverPath) {
|
||||
if (!feed.coverPath) {
|
||||
res.sendStatus(404)
|
||||
return
|
||||
}
|
||||
|
||||
const extname = Path.extname(feedData.mediaCoverPath).toLowerCase().slice(1)
|
||||
const extname = Path.extname(feedData.coverPath).toLowerCase().slice(1)
|
||||
res.type(`image/${extname}`)
|
||||
var readStream = fs.createReadStream(feedData.mediaCoverPath)
|
||||
var readStream = fs.createReadStream(feedData.coverPath)
|
||||
readStream.pipe(res)
|
||||
}
|
||||
|
||||
openFeed(userId, slug, libraryItem, serverAddress) {
|
||||
const media = libraryItem.media
|
||||
const mediaMetadata = media.metadata
|
||||
const isPodcast = libraryItem.mediaType === 'podcast'
|
||||
|
||||
const feedUrl = `${serverAddress}/feed/${slug}`
|
||||
const author = isPodcast ? mediaMetadata.author : mediaMetadata.authorName
|
||||
|
||||
const feed = new Podcast({
|
||||
title: mediaMetadata.title,
|
||||
description: mediaMetadata.description,
|
||||
feedUrl,
|
||||
siteUrl: `${serverAddress}/items/${libraryItem.id}`,
|
||||
imageUrl: media.coverPath ? `${serverAddress}/feed/${slug}/cover` : `${serverAddress}/Logo.png`,
|
||||
author: author || 'advplyr',
|
||||
language: 'en'
|
||||
})
|
||||
|
||||
if (isPodcast) { // PODCAST EPISODES
|
||||
media.episodes.forEach((episode) => {
|
||||
var contentUrl = episode.audioTrack.contentUrl.replace(/\\/g, '/')
|
||||
contentUrl = contentUrl.replace(`/s/item/${libraryItem.id}`, `/feed/${slug}/item`)
|
||||
|
||||
feed.addItem({
|
||||
title: episode.title,
|
||||
description: episode.description || '',
|
||||
enclosure: {
|
||||
url: `${serverAddress}${contentUrl}`,
|
||||
type: episode.audioTrack.mimeType,
|
||||
size: episode.size
|
||||
},
|
||||
date: episode.pubDate || '',
|
||||
url: `${serverAddress}${contentUrl}`,
|
||||
author: author || 'advplyr'
|
||||
})
|
||||
})
|
||||
} else { // AUDIOBOOK EPISODES
|
||||
|
||||
// Example: <pubDate>Fri, 04 Feb 2015 00:00:00 GMT</pubDate>
|
||||
const audiobookPubDate = date.format(new Date(libraryItem.addedAt), 'ddd, DD MMM YYYY HH:mm:ss [GMT]')
|
||||
|
||||
media.tracks.forEach((audioTrack) => {
|
||||
var contentUrl = audioTrack.contentUrl.replace(/\\/g, '/')
|
||||
contentUrl = contentUrl.replace(`/s/item/${libraryItem.id}`, `/feed/${slug}/item`)
|
||||
|
||||
var title = audioTrack.title
|
||||
if (media.chapters.length) {
|
||||
// If audio track start and chapter start are within 1 seconds of eachother then use the chapter title
|
||||
var matchingChapter = media.chapters.find(ch => Math.abs(ch.start - audioTrack.startOffset) < 1)
|
||||
if (matchingChapter && matchingChapter.title) title = matchingChapter.title
|
||||
}
|
||||
|
||||
feed.addItem({
|
||||
title,
|
||||
description: '',
|
||||
enclosure: {
|
||||
url: `${serverAddress}${contentUrl}`,
|
||||
type: audioTrack.mimeType,
|
||||
size: audioTrack.metadata.size
|
||||
},
|
||||
date: audiobookPubDate,
|
||||
url: `${serverAddress}${contentUrl}`,
|
||||
author: author || 'advplyr'
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const feedData = {
|
||||
id: slug,
|
||||
slug,
|
||||
userId,
|
||||
libraryItemId: libraryItem.id,
|
||||
libraryItemPath: libraryItem.path,
|
||||
mediaCoverPath: media.coverPath,
|
||||
serverAddress: serverAddress,
|
||||
feedUrl,
|
||||
feed
|
||||
}
|
||||
this.feeds[slug] = feedData
|
||||
return feedData
|
||||
}
|
||||
|
||||
openFeedForItem(user, libraryItem, options) {
|
||||
async openFeedForItem(user, libraryItem, options) {
|
||||
const serverAddress = options.serverAddress
|
||||
const slug = options.slug
|
||||
|
||||
|
@ -153,24 +90,29 @@ class RssFeedManager {
|
|||
}
|
||||
}
|
||||
|
||||
const feedData = this.openFeed(user.id, slug, libraryItem, serverAddress)
|
||||
Logger.debug(`[RssFeedManager] Opened RSS feed ${feedData.feedUrl}`)
|
||||
this.emitter('rss_feed_open', { libraryItemId: libraryItem.id, feedUrl: feedData.feedUrl })
|
||||
return feedData
|
||||
const feed = new Feed()
|
||||
feed.setFromItem(user.id, slug, libraryItem, serverAddress)
|
||||
this.feeds[feed.id] = feed
|
||||
|
||||
Logger.debug(`[RssFeedManager] Opened RSS feed ${feed.feedUrl}`)
|
||||
await this.db.insertEntity('feed', feed)
|
||||
this.emitter('rss_feed_open', { entityType: feed.entityType, entityId: feed.entityId, feedUrl: feed.feedUrl })
|
||||
return feed
|
||||
}
|
||||
|
||||
closeFeedForItem(libraryItemId) {
|
||||
var feed = this.findFeedForItem(libraryItemId)
|
||||
if (!feed) return
|
||||
this.closeRssFeed(feed.id)
|
||||
return this.closeRssFeed(feed.id)
|
||||
}
|
||||
|
||||
closeRssFeed(id) {
|
||||
async closeRssFeed(id) {
|
||||
if (!this.feeds[id]) return
|
||||
var feedData = this.feeds[id]
|
||||
this.emitter('rss_feed_closed', { libraryItemId: feedData.libraryItemId, feedUrl: feedData.feedUrl })
|
||||
var feed = this.feeds[id]
|
||||
await this.db.removeEntity('feed', id)
|
||||
this.emitter('rss_feed_closed', { entityType: feed.entityType, entityId: feed.entityId, feedUrl: feed.feedUrl })
|
||||
delete this.feeds[id]
|
||||
Logger.info(`[RssFeedManager] Closed RSS feed "${feedData.feedUrl}"`)
|
||||
Logger.info(`[RssFeedManager] Closed RSS feed "${feed.feedUrl}"`)
|
||||
}
|
||||
}
|
||||
module.exports = RssFeedManager
|
Loading…
Add table
Add a link
Reference in a new issue