2023-11-01 11:51:39 +01:00
|
|
|
const Path = require('path')
|
2024-07-12 00:49:05 +02:00
|
|
|
const uuidv4 = require('uuid').v4
|
2022-07-07 02:18:27 +02:00
|
|
|
const date = require('../libs/dateAndTime')
|
2022-06-08 02:25:14 +02:00
|
|
|
const { secondsToTimestamp } = require('../utils/index')
|
2022-06-08 01:29:43 +02:00
|
|
|
|
|
|
|
class FeedEpisode {
|
|
|
|
constructor(episode) {
|
|
|
|
this.id = null
|
|
|
|
|
|
|
|
this.title = null
|
|
|
|
this.description = null
|
|
|
|
this.enclosure = null
|
|
|
|
this.pubDate = null
|
|
|
|
this.link = null
|
|
|
|
this.author = null
|
|
|
|
this.explicit = null
|
|
|
|
this.duration = null
|
2023-02-22 19:22:52 +01:00
|
|
|
this.season = null
|
|
|
|
this.episode = null
|
|
|
|
this.episodeType = null
|
2022-06-08 01:29:43 +02:00
|
|
|
|
|
|
|
this.libraryItemId = null
|
|
|
|
this.episodeId = null
|
|
|
|
this.trackIndex = null
|
|
|
|
this.fullPath = null
|
|
|
|
|
|
|
|
if (episode) {
|
|
|
|
this.construct(episode)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
construct(episode) {
|
|
|
|
this.id = episode.id
|
|
|
|
this.title = episode.title
|
|
|
|
this.description = episode.description
|
|
|
|
this.enclosure = episode.enclosure ? { ...episode.enclosure } : null
|
|
|
|
this.pubDate = episode.pubDate
|
|
|
|
this.link = episode.link
|
|
|
|
this.author = episode.author
|
|
|
|
this.explicit = episode.explicit
|
|
|
|
this.duration = episode.duration
|
2023-02-22 19:22:52 +01:00
|
|
|
this.season = episode.season
|
|
|
|
this.episode = episode.episode
|
|
|
|
this.episodeType = episode.episodeType
|
2022-06-08 01:29:43 +02:00
|
|
|
this.libraryItemId = episode.libraryItemId
|
|
|
|
this.episodeId = episode.episodeId || null
|
|
|
|
this.trackIndex = episode.trackIndex || 0
|
|
|
|
this.fullPath = episode.fullPath
|
|
|
|
}
|
|
|
|
|
|
|
|
toJSON() {
|
|
|
|
return {
|
|
|
|
id: this.id,
|
|
|
|
title: this.title,
|
|
|
|
description: this.description,
|
|
|
|
enclosure: this.enclosure ? { ...this.enclosure } : null,
|
|
|
|
pubDate: this.pubDate,
|
|
|
|
link: this.link,
|
|
|
|
author: this.author,
|
|
|
|
explicit: this.explicit,
|
|
|
|
duration: this.duration,
|
2023-02-22 19:22:52 +01:00
|
|
|
season: this.season,
|
|
|
|
episode: this.episode,
|
|
|
|
episodeType: this.episodeType,
|
2022-06-08 01:29:43 +02:00
|
|
|
libraryItemId: this.libraryItemId,
|
|
|
|
episodeId: this.episodeId,
|
|
|
|
trackIndex: this.trackIndex,
|
|
|
|
fullPath: this.fullPath
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setFromPodcastEpisode(libraryItem, serverAddress, slug, episode, meta) {
|
2023-11-01 11:51:39 +01:00
|
|
|
const contentFileExtension = Path.extname(episode.audioFile.metadata.filename)
|
|
|
|
const contentUrl = `/feed/${slug}/item/${episode.id}/media${contentFileExtension}`
|
2022-06-08 01:29:43 +02:00
|
|
|
const media = libraryItem.media
|
|
|
|
const mediaMetadata = media.metadata
|
|
|
|
|
|
|
|
this.id = episode.id
|
|
|
|
this.title = episode.title
|
|
|
|
this.description = episode.description || ''
|
|
|
|
this.enclosure = {
|
2024-12-07 18:27:37 +01:00
|
|
|
url: `${contentUrl}`,
|
2022-06-08 01:29:43 +02:00
|
|
|
type: episode.audioTrack.mimeType,
|
|
|
|
size: episode.size
|
|
|
|
}
|
|
|
|
this.pubDate = episode.pubDate
|
|
|
|
this.link = meta.link
|
|
|
|
this.author = meta.author
|
|
|
|
this.explicit = mediaMetadata.explicit
|
|
|
|
this.duration = episode.duration
|
2023-02-22 19:22:52 +01:00
|
|
|
this.season = episode.season
|
|
|
|
this.episode = episode.episode
|
|
|
|
this.episodeType = episode.episodeType
|
2022-06-08 01:29:43 +02:00
|
|
|
this.libraryItemId = libraryItem.id
|
|
|
|
this.episodeId = episode.id
|
|
|
|
this.trackIndex = 0
|
|
|
|
this.fullPath = episode.audioFile.metadata.path
|
|
|
|
}
|
|
|
|
|
2024-01-24 00:51:34 +01:00
|
|
|
/**
|
2024-07-12 00:49:05 +02:00
|
|
|
*
|
|
|
|
* @param {import('../objects/LibraryItem')} libraryItem
|
|
|
|
* @param {string} serverAddress
|
|
|
|
* @param {string} slug
|
|
|
|
* @param {import('../objects/files/AudioTrack')} audioTrack
|
|
|
|
* @param {Object} meta
|
|
|
|
* @param {boolean} useChapterTitles
|
|
|
|
* @param {string} [pubDateOverride] Used for series & collections to ensure correct episode order
|
2024-01-24 00:51:34 +01:00
|
|
|
*/
|
2024-07-12 00:49:05 +02:00
|
|
|
setFromAudiobookTrack(libraryItem, serverAddress, slug, audioTrack, meta, useChapterTitles, pubDateOverride = null) {
|
2022-06-08 01:29:43 +02:00
|
|
|
// Example: <pubDate>Fri, 04 Feb 2015 00:00:00 GMT</pubDate>
|
2024-07-12 00:49:05 +02:00
|
|
|
let timeOffset = isNaN(audioTrack.index) ? 0 : Number(audioTrack.index) * 1000 // Offset pubdate to ensure correct order
|
2023-07-06 01:18:37 +02:00
|
|
|
let episodeId = uuidv4()
|
2022-12-27 00:48:39 +01:00
|
|
|
|
2022-12-16 00:36:29 +01:00
|
|
|
// e.g. Track 1 will have a pub date before Track 2
|
2024-07-12 00:49:05 +02:00
|
|
|
const audiobookPubDate = pubDateOverride || date.format(new Date(libraryItem.addedAt + timeOffset), 'ddd, DD MMM YYYY HH:mm:ss [GMT]')
|
2022-06-08 01:29:43 +02:00
|
|
|
|
2023-11-01 11:51:39 +01:00
|
|
|
const contentFileExtension = Path.extname(audioTrack.metadata.filename)
|
|
|
|
const contentUrl = `/feed/${slug}/item/${episodeId}/media${contentFileExtension}`
|
2022-06-08 01:29:43 +02:00
|
|
|
const media = libraryItem.media
|
|
|
|
const mediaMetadata = media.metadata
|
|
|
|
|
2022-11-25 13:21:50 +01:00
|
|
|
let title = audioTrack.title
|
2024-07-12 00:49:05 +02:00
|
|
|
if (libraryItem.media.tracks.length == 1) {
|
|
|
|
// If audiobook is a single file, use book title instead of chapter/file title
|
2022-11-25 13:21:50 +01:00
|
|
|
title = libraryItem.media.metadata.title
|
2022-11-25 08:10:20 +01:00
|
|
|
} else {
|
2024-01-24 00:51:34 +01:00
|
|
|
if (useChapterTitles) {
|
2022-11-25 08:10:20 +01:00
|
|
|
// If audio track start and chapter start are within 1 seconds of eachother then use the chapter title
|
2024-07-12 00:49:05 +02:00
|
|
|
const matchingChapter = libraryItem.media.chapters.find((ch) => Math.abs(ch.start - audioTrack.startOffset) < 1)
|
2024-01-24 00:51:34 +01:00
|
|
|
if (matchingChapter?.title) title = matchingChapter.title
|
2022-11-25 08:10:20 +01:00
|
|
|
}
|
2022-06-08 01:29:43 +02:00
|
|
|
}
|
|
|
|
|
2023-03-05 22:26:18 +01:00
|
|
|
this.id = episodeId
|
2022-06-08 01:29:43 +02:00
|
|
|
this.title = title
|
|
|
|
this.description = mediaMetadata.description || ''
|
|
|
|
this.enclosure = {
|
2024-12-07 18:27:37 +01:00
|
|
|
url: `${contentUrl}`,
|
2022-06-08 01:29:43 +02:00
|
|
|
type: audioTrack.mimeType,
|
|
|
|
size: audioTrack.metadata.size
|
|
|
|
}
|
|
|
|
this.pubDate = audiobookPubDate
|
|
|
|
this.link = meta.link
|
|
|
|
this.author = meta.author
|
|
|
|
this.explicit = mediaMetadata.explicit
|
|
|
|
this.duration = audioTrack.duration
|
|
|
|
this.libraryItemId = libraryItem.id
|
|
|
|
this.episodeId = null
|
|
|
|
this.trackIndex = audioTrack.index
|
|
|
|
this.fullPath = audioTrack.metadata.path
|
|
|
|
}
|
|
|
|
|
2024-12-07 18:27:37 +01:00
|
|
|
getRSSData(hostPrefix) {
|
2022-06-08 01:29:43 +02:00
|
|
|
return {
|
|
|
|
title: this.title,
|
|
|
|
description: this.description || '',
|
2024-12-07 18:27:37 +01:00
|
|
|
url: `${hostPrefix}${this.link}`,
|
|
|
|
guid: `${hostPrefix}${this.enclosure.url}`,
|
2022-06-08 02:25:14 +02:00
|
|
|
author: this.author,
|
|
|
|
date: this.pubDate,
|
2024-12-07 18:27:37 +01:00
|
|
|
enclosure: {
|
|
|
|
url: `${hostPrefix}${this.enclosure.url}`,
|
|
|
|
type: this.enclosure.type,
|
|
|
|
size: this.enclosure.size
|
|
|
|
},
|
2022-06-08 02:25:14 +02:00
|
|
|
custom_elements: [
|
|
|
|
{ 'itunes:author': this.author },
|
|
|
|
{ 'itunes:duration': secondsToTimestamp(this.duration) },
|
|
|
|
{ 'itunes:summary': this.description || '' },
|
|
|
|
{
|
2024-07-12 00:49:05 +02:00
|
|
|
'itunes:explicit': !!this.explicit
|
2023-02-22 19:22:52 +01:00
|
|
|
},
|
2024-07-12 00:49:05 +02:00
|
|
|
{ 'itunes:episodeType': this.episodeType },
|
|
|
|
{ 'itunes:season': this.season },
|
|
|
|
{ 'itunes:episode': this.episode }
|
2022-06-08 02:25:14 +02:00
|
|
|
]
|
2022-06-08 01:29:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-02-22 19:22:52 +01:00
|
|
|
module.exports = FeedEpisode
|