From 7f88d4b0369fa8f5fb4bd51e5a2abc9a9de7b0b1 Mon Sep 17 00:00:00 2001
From: Harry Rose
Date: Tue, 17 Mar 2026 19:43:09 +0000
Subject: [PATCH] Early out if the description doesn't contain and timestamps
---
server/models/PodcastEpisode.js | 6 +++++-
.../parsers/parsePodcastDescriptionForChapters.js | 8 ++++++--
.../parsePodcastDescriptionForChapters.test.js | 13 +++++++++++++
3 files changed, 24 insertions(+), 3 deletions(-)
diff --git a/server/models/PodcastEpisode.js b/server/models/PodcastEpisode.js
index 7c107a87..fdef2c50 100644
--- a/server/models/PodcastEpisode.js
+++ b/server/models/PodcastEpisode.js
@@ -91,8 +91,12 @@ class PodcastEpisode extends Model {
Logger.debug("[PodcastEpisode] New episode doesn't have chapters, attempting to generate them from timestamps", rssPodcastEpisode.title)
try {
podcastEpisode.chapters = parsePodcastDescriptionForChapters.parse(podcastEpisode.description, podcastEpisode.audioFile.duration)
+
+ if (podcastEpisode.chapters.length > 0) {
+ Logger.info(`[PodcastEpisode] Successfully generated ${podcastEpisode.chapters.length} chapters`)
+ }
} catch (error) {
- Logger.error(`[PodcastEpisode] createFromRssPodcastEpisode: Failed to auto generate chapters for "${podcastEpisode.title}"`, error)
+ Logger.error(`[PodcastEpisode] createFromRssPodcastEpisode: Failed to generate chapters for "${podcastEpisode.title}"`, error)
}
}
diff --git a/server/utils/parsers/parsePodcastDescriptionForChapters.js b/server/utils/parsers/parsePodcastDescriptionForChapters.js
index 53585d64..1fa59f83 100644
--- a/server/utils/parsers/parsePodcastDescriptionForChapters.js
+++ b/server/utils/parsers/parsePodcastDescriptionForChapters.js
@@ -36,6 +36,12 @@ module.exports.parse = (podcastDescription, audioDurationSecs) => {
// Split on "
", "
", "\n",
const descriptionLineSplitRegex = /\<\s*\/\s*p\s*\>|\<\s*br\s*\/\>|\n|\<\s*\/\s*li\s*\>/
+ // Early out if there aren't any timestamps in the entire description
+ if (timestampRegex.exec(podcastDescription) == null) {
+ Logger.debug('No timestamps found in description, bailing out early')
+ return []
+ }
+
var descriptionLines = podcastDescription.split(descriptionLineSplitRegex)
var newChapters = []
@@ -98,8 +104,6 @@ module.exports.parse = (podcastDescription, audioDurationSecs) => {
newChapters[newChapters.length - 1].end = audioDurationSecs
}
- Logger.info(`Successfully generated ${newChapters.length} chapters`)
-
if (newChapters.length == 1) {
throw new Error('Only one chapter found, treating as invalid description')
}
diff --git a/test/server/utils/parsers/parsePodcastDescriptionForChapters.test.js b/test/server/utils/parsers/parsePodcastDescriptionForChapters.test.js
index 24ca3f0a..c4765415 100644
--- a/test/server/utils/parsers/parsePodcastDescriptionForChapters.test.js
+++ b/test/server/utils/parsers/parsePodcastDescriptionForChapters.test.js
@@ -1,8 +1,21 @@
const chai = require('chai')
const expect = chai.expect
const parsePodcastDescriptionForChapters = require('../../../../server/utils/parsers/parsePodcastDescriptionForChapters')
+const sinon = require('sinon')
+const Logger = require('../../../../server/Logger')
describe('parsePodcastDescriptionForChapters', () => {
+ it("should early out if description doens't contain timestamps", () => {
+ let loggerDebugStub = sinon.stub(Logger, 'debug')
+ let description = 'Introduction text paragraph 1
Introduction text paragraph 2
'
+ let chapters = parsePodcastDescriptionForChapters.parse(description, 1000)
+
+ expect(chapters).to.be.empty
+ expect(loggerDebugStub.calledWith('No timestamps found in description, bailing out early')).to.be.true
+
+ sinon.restore()
+ })
+
var testCasesTestingSuccess = [
{
testName: 'Should handle descriptions using html paragraphs',