From 3906dca04e3fd71842334d530e74789a7aed6c06 Mon Sep 17 00:00:00 2001 From: advplyr Date: Tue, 23 Jan 2024 17:51:34 -0600 Subject: [PATCH] Update:RSS feeds only use chapter titles for episode titles if all audio tracks match chapter times #2543 --- server/objects/Feed.js | 36 +++++++++++++++++++++++++++++------ server/objects/FeedEpisode.js | 18 ++++++++++++++---- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/server/objects/Feed.js b/server/objects/Feed.js index 08a602aecc..2fac5d910b 100644 --- a/server/objects/Feed.js +++ b/server/objects/Feed.js @@ -84,6 +84,24 @@ class Feed { return episode.fullPath } + /** + * If chapters for an audiobook match the audio tracks then use chapter titles instead of audio file names + * + * @param {import('../objects/LibraryItem')} libraryItem + * @returns {boolean} + */ + checkUseChapterTitlesForEpisodes(libraryItem) { + const tracks = libraryItem.media.tracks + const chapters = libraryItem.media.chapters + if (tracks.length !== chapters.length) return false + for (let i = 0; i < tracks.length; i++) { + if (Math.abs(chapters[i].start - tracks[i].startOffset) >= 1) { + return false + } + } + return true + } + setFromItem(userId, slug, libraryItem, serverAddress, preventIndexing = true, ownerName = null, ownerEmail = null) { const media = libraryItem.media const mediaMetadata = media.metadata @@ -128,9 +146,10 @@ class Feed { this.episodes.push(feedEpisode) }) } else { // AUDIOBOOK EPISODES + const useChapterTitles = this.checkUseChapterTitlesForEpisodes(libraryItem) media.tracks.forEach((audioTrack) => { const feedEpisode = new FeedEpisode() - feedEpisode.setFromAudiobookTrack(libraryItem, serverAddress, slug, audioTrack, this.meta) + feedEpisode.setFromAudiobookTrack(libraryItem, serverAddress, slug, audioTrack, this.meta, useChapterTitles) this.episodes.push(feedEpisode) }) } @@ -168,9 +187,10 @@ class Feed { this.episodes.push(feedEpisode) }) } else { // AUDIOBOOK EPISODES + const useChapterTitles = this.checkUseChapterTitlesForEpisodes(libraryItem) media.tracks.forEach((audioTrack) => { const feedEpisode = new FeedEpisode() - feedEpisode.setFromAudiobookTrack(libraryItem, this.serverAddress, this.slug, audioTrack, this.meta) + feedEpisode.setFromAudiobookTrack(libraryItem, this.serverAddress, this.slug, audioTrack, this.meta, useChapterTitles) this.episodes.push(feedEpisode) }) } @@ -214,9 +234,10 @@ class Feed { itemsWithTracks.forEach((item, index) => { if (item.updatedAt > this.entityUpdatedAt) this.entityUpdatedAt = item.updatedAt + const useChapterTitles = this.checkUseChapterTitlesForEpisodes(item) item.media.tracks.forEach((audioTrack) => { const feedEpisode = new FeedEpisode() - feedEpisode.setFromAudiobookTrack(item, serverAddress, slug, audioTrack, this.meta, index) + feedEpisode.setFromAudiobookTrack(item, serverAddress, slug, audioTrack, this.meta, useChapterTitles, index) this.episodes.push(feedEpisode) }) }) @@ -245,9 +266,10 @@ class Feed { itemsWithTracks.forEach((item, index) => { if (item.updatedAt > this.entityUpdatedAt) this.entityUpdatedAt = item.updatedAt + const useChapterTitles = this.checkUseChapterTitlesForEpisodes(item) item.media.tracks.forEach((audioTrack) => { const feedEpisode = new FeedEpisode() - feedEpisode.setFromAudiobookTrack(item, this.serverAddress, this.slug, audioTrack, this.meta, index) + feedEpisode.setFromAudiobookTrack(item, this.serverAddress, this.slug, audioTrack, this.meta, useChapterTitles, index) this.episodes.push(feedEpisode) }) }) @@ -295,9 +317,10 @@ class Feed { itemsWithTracks.forEach((item, index) => { if (item.updatedAt > this.entityUpdatedAt) this.entityUpdatedAt = item.updatedAt + const useChapterTitles = this.checkUseChapterTitlesForEpisodes(item) item.media.tracks.forEach((audioTrack) => { const feedEpisode = new FeedEpisode() - feedEpisode.setFromAudiobookTrack(item, serverAddress, slug, audioTrack, this.meta, index) + feedEpisode.setFromAudiobookTrack(item, serverAddress, slug, audioTrack, this.meta, useChapterTitles, index) this.episodes.push(feedEpisode) }) }) @@ -329,9 +352,10 @@ class Feed { itemsWithTracks.forEach((item, index) => { if (item.updatedAt > this.entityUpdatedAt) this.entityUpdatedAt = item.updatedAt + const useChapterTitles = this.checkUseChapterTitlesForEpisodes(item) item.media.tracks.forEach((audioTrack) => { const feedEpisode = new FeedEpisode() - feedEpisode.setFromAudiobookTrack(item, this.serverAddress, this.slug, audioTrack, this.meta, index) + feedEpisode.setFromAudiobookTrack(item, this.serverAddress, this.slug, audioTrack, this.meta, useChapterTitles, index) this.episodes.push(feedEpisode) }) }) diff --git a/server/objects/FeedEpisode.js b/server/objects/FeedEpisode.js index b87cf88f5f..50e27cf619 100644 --- a/server/objects/FeedEpisode.js +++ b/server/objects/FeedEpisode.js @@ -97,7 +97,17 @@ class FeedEpisode { this.fullPath = episode.audioFile.metadata.path } - setFromAudiobookTrack(libraryItem, serverAddress, slug, audioTrack, meta, additionalOffset = null) { + /** + * + * @param {import('../objects/LibraryItem')} libraryItem + * @param {string} serverAddress + * @param {string} slug + * @param {import('../objects/files/AudioTrack')} audioTrack + * @param {Object} meta + * @param {boolean} useChapterTitles + * @param {number} [additionalOffset] + */ + setFromAudiobookTrack(libraryItem, serverAddress, slug, audioTrack, meta, useChapterTitles, additionalOffset = null) { // Example: Fri, 04 Feb 2015 00:00:00 GMT let timeOffset = isNaN(audioTrack.index) ? 0 : (Number(audioTrack.index) * 1000) // Offset pubdate to ensure correct order let episodeId = uuidv4() @@ -119,10 +129,10 @@ class FeedEpisode { if (libraryItem.media.tracks.length == 1) { // If audiobook is a single file, use book title instead of chapter/file title title = libraryItem.media.metadata.title } else { - if (libraryItem.media.chapters.length) { + if (useChapterTitles) { // If audio track start and chapter start are within 1 seconds of eachother then use the chapter title - var matchingChapter = libraryItem.media.chapters.find(ch => Math.abs(ch.start - audioTrack.startOffset) < 1) - if (matchingChapter && matchingChapter.title) title = matchingChapter.title + const matchingChapter = libraryItem.media.chapters.find(ch => Math.abs(ch.start - audioTrack.startOffset) < 1) + if (matchingChapter?.title) title = matchingChapter.title } }