Add:OPML Export #1260

This commit is contained in:
advplyr 2023-05-28 15:10:34 -05:00
parent 019063e6f4
commit 15aaf2863c
16 changed files with 124 additions and 40 deletions

View file

@ -848,6 +848,12 @@ class LibraryController {
res.json(payload)
}
getOPMLFile(req, res) {
const opmlText = this.podcastManager.generateOPMLFileText(req.libraryItems)
res.type('application/xml')
res.send(opmlText)
}
middleware(req, res, next) {
if (!req.user.checkCanAccessLibrary(req.params.id)) {
Logger.warn(`[LibraryController] Library ${req.params.id} not accessible to user ${req.user.username}`)

View file

@ -109,7 +109,7 @@ class PodcastController {
res.json({ podcast })
}
async getOPMLFeeds(req, res) {
async getFeedsFromOPMLText(req, res) {
if (!req.body.opmlText) {
return res.sendStatus(400)
}

View file

@ -8,6 +8,7 @@ const { removeFile, downloadFile } = require('../utils/fileUtils')
const filePerms = require('../utils/filePerms')
const { levenshteinDistance } = require('../utils/index')
const opmlParser = require('../utils/parsers/parseOPML')
const opmlGenerator = require('../utils/generators/opmlGenerator')
const prober = require('../utils/prober')
const ffmpegHelpers = require('../utils/ffmpegHelpers')
@ -373,6 +374,10 @@ class PodcastManager {
}
}
generateOPMLFileText(libraryItems) {
return opmlGenerator.generate(libraryItems)
}
getDownloadQueueDetails(libraryId = null) {
let _currentDownload = this.currentDownload
if (libraryId && _currentDownload?.libraryId !== libraryId) _currentDownload = null

View file

@ -2,7 +2,7 @@ const fs = require('../libs/fsExtra')
const Path = require('path')
const { version } = require('../../package.json')
const Logger = require('../Logger')
const abmetadataGenerator = require('../utils/abmetadataGenerator')
const abmetadataGenerator = require('../utils/generators/abmetadataGenerator')
const LibraryFile = require('./files/LibraryFile')
const Book = require('./mediaTypes/Book')
const Podcast = require('./mediaTypes/Podcast')

View file

@ -10,7 +10,7 @@ const Ffmpeg = require('../libs/fluentFfmpeg')
const { secondsToTimestamp } = require('../utils/index')
const { writeConcatFile } = require('../utils/ffmpegHelpers')
const { AudioMimeType } = require('../utils/constants')
const hlsPlaylistGenerator = require('../utils/hlsPlaylistGenerator')
const hlsPlaylistGenerator = require('../utils/generators/hlsPlaylistGenerator')
const AudioTrack = require('./files/AudioTrack')
class Stream extends EventEmitter {

View file

@ -4,7 +4,7 @@ const BookMetadata = require('../metadata/BookMetadata')
const { areEquivalent, copyValue, cleanStringForSearch } = require('../../utils/index')
const { parseOpfMetadataXML } = require('../../utils/parsers/parseOpfMetadata')
const { parseOverdriveMediaMarkersAsChapters } = require('../../utils/parsers/parseOverdriveMediaMarkers')
const abmetadataGenerator = require('../../utils/abmetadataGenerator')
const abmetadataGenerator = require('../../utils/generators/abmetadataGenerator')
const { readTextFile, filePathToPOSIX } = require('../../utils/fileUtils')
const AudioFile = require('../files/AudioFile')
const AudioTrack = require('../files/AudioTrack')

View file

@ -2,7 +2,7 @@ const Logger = require('../../Logger')
const PodcastEpisode = require('../entities/PodcastEpisode')
const PodcastMetadata = require('../metadata/PodcastMetadata')
const { areEquivalent, copyValue, cleanStringForSearch } = require('../../utils/index')
const abmetadataGenerator = require('../../utils/abmetadataGenerator')
const abmetadataGenerator = require('../../utils/generators/abmetadataGenerator')
const { readTextFile, filePathToPOSIX } = require('../../utils/fileUtils')
const { createNewSortInstance } = require('../../libs/fastSort')
const naturalSort = createNewSortInstance({

View file

@ -90,7 +90,7 @@ class ApiRouter {
this.router.get('/libraries/:id/matchall', LibraryController.middleware.bind(this), LibraryController.matchAll.bind(this))
this.router.post('/libraries/:id/scan', LibraryController.middleware.bind(this), LibraryController.scan.bind(this))
this.router.get('/libraries/:id/recent-episodes', LibraryController.middleware.bind(this), LibraryController.getRecentEpisodes.bind(this))
this.router.get('/libraries/:id/opml', LibraryController.middleware.bind(this), LibraryController.getOPMLFile.bind(this))
this.router.post('/libraries/order', LibraryController.reorder.bind(this))
//
@ -236,7 +236,7 @@ class ApiRouter {
//
this.router.post('/podcasts', PodcastController.create.bind(this))
this.router.post('/podcasts/feed', PodcastController.getPodcastFeed.bind(this))
this.router.post('/podcasts/opml', PodcastController.getOPMLFeeds.bind(this))
this.router.post('/podcasts/opml', PodcastController.getFeedsFromOPMLText.bind(this))
this.router.get('/podcasts/:id/checknew', PodcastController.middleware.bind(this), PodcastController.checkNewEpisodes.bind(this))
this.router.get('/podcasts/:id/downloads', PodcastController.middleware.bind(this), PodcastController.getEpisodeDownloads.bind(this))
this.router.get('/podcasts/:id/clear-queue', PodcastController.middleware.bind(this), PodcastController.clearEpisodeDownloadQueue.bind(this))

View file

@ -1,9 +1,9 @@
const fs = require('../libs/fsExtra')
const filePerms = require('./filePerms')
const package = require('../../package.json')
const Logger = require('../Logger')
const { getId } = require('./index')
const areEquivalent = require('../utils/areEquivalent')
const fs = require('../../libs/fsExtra')
const filePerms = require('../filePerms')
const package = require('../../../package.json')
const Logger = require('../../Logger')
const { getId } = require('../index')
const areEquivalent = require('../areEquivalent')
const CurrentAbMetadataVersion = 2

View file

@ -1,4 +1,4 @@
const fs = require('../libs/fsExtra')
const fs = require('../../libs/fsExtra')
function getPlaylistStr(segmentName, duration, segmentLength, hlsSegmentType, token) {
var ext = hlsSegmentType === 'fmp4' ? 'm4s' : 'ts'

View file

@ -0,0 +1,52 @@
const xml = require('../../libs/xml')
module.exports.generate = (libraryItems, indent = true) => {
const bodyItems = []
libraryItems.forEach((item) => {
if (!item.media.metadata.feedUrl) return
const feedAttributes = {
type: 'rss',
text: item.media.metadata.title,
title: item.media.metadata.title,
xmlUrl: item.media.metadata.feedUrl
}
if (item.media.metadata.description) {
feedAttributes.description = item.media.metadata.description
}
if (item.media.metadata.itunesPageUrl) {
feedAttributes.htmlUrl = item.media.metadata.itunesPageUrl
}
if (item.media.metadata.language) {
feedAttributes.language = item.media.metadata.language
}
bodyItems.push({
outline: {
_attr: feedAttributes
}
})
})
const data = [
{
opml: [
{
_attr: {
version: '1.0'
}
},
{
head: [
{
title: 'Audiobookshelf Podcast Subscriptions'
}
]
},
{
body: bodyItems
}
]
}
]
return '<?xml version="1.0" encoding="UTF-8"?>\n' + xml(data, indent)
}