Merge pull request #4422 from advplyr/podcast_episode_duration

Show duration in episode view modal & episode feed modal
This commit is contained in:
advplyr 2025-06-19 17:35:36 -05:00 committed by GitHub
commit 28404f37b8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 24 additions and 4 deletions

View File

@ -35,7 +35,14 @@
<widgets-podcast-type-indicator :type="episode.episodeType" /> <widgets-podcast-type-indicator :type="episode.episodeType" />
</div> </div>
<p v-if="episode.subtitle" class="mb-1 text-sm text-gray-300 line-clamp-2">{{ episode.subtitle }}</p> <p v-if="episode.subtitle" class="mb-1 text-sm text-gray-300 line-clamp-2">{{ episode.subtitle }}</p>
<p class="text-xs text-gray-300">Published {{ episode.publishedAt ? $dateDistanceFromNow(episode.publishedAt) : 'Unknown' }}</p> <div class="flex items-center space-x-2">
<!-- published -->
<p class="text-xs text-gray-300 w-40">Published {{ episode.publishedAt ? $dateDistanceFromNow(episode.publishedAt) : 'Unknown' }}</p>
<!-- duration -->
<p v-if="episode.durationSeconds && !isNaN(episode.durationSeconds)" class="text-xs text-gray-300 min-w-28">{{ $strings.LabelDuration }}: {{ $elapsedPretty(episode.durationSeconds) }}</p>
<!-- size -->
<p v-if="episode.enclosure?.length && !isNaN(episode.enclosure.length) && Number(episode.enclosure.length) > 0" class="text-xs text-gray-300">{{ $strings.LabelSize }}: {{ $bytesPretty(Number(episode.enclosure.length)) }}</p>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -34,6 +34,12 @@
{{ audioFileSize }} {{ audioFileSize }}
</p> </p>
</div> </div>
<div class="grow">
<p class="font-semibold text-xs mb-1">{{ $strings.LabelDuration }}</p>
<p class="mb-2 text-xs">
{{ audioFileDuration }}
</p>
</div>
</div> </div>
</div> </div>
</modals-modal> </modals-modal>
@ -90,6 +96,10 @@ export default {
return this.$bytesPretty(size) return this.$bytesPretty(size)
}, },
audioFileDuration() {
const duration = this.episode.duration || 0
return this.$elapsedPretty(duration)
},
bookCoverAspectRatio() { bookCoverAspectRatio() {
return this.$store.getters['libraries/getBookCoverAspectRatio'] return this.$store.getters['libraries/getBookCoverAspectRatio']
} }

View File

@ -25,6 +25,7 @@ const Fuse = require('../libs/fusejs')
* @property {string} episode * @property {string} episode
* @property {string} author * @property {string} author
* @property {string} duration * @property {string} duration
* @property {number|null} durationSeconds - Parsed from duration string if duration is valid
* @property {string} explicit * @property {string} explicit
* @property {number} publishedAt - Unix timestamp * @property {number} publishedAt - Unix timestamp
* @property {{ url: string, type?: string, length?: string }} enclosure * @property {{ url: string, type?: string, length?: string }} enclosure
@ -217,8 +218,9 @@ function extractEpisodeData(item) {
}) })
// Extract psc:chapters if duration is set // Extract psc:chapters if duration is set
let episodeDuration = !isNaN(episode.duration) ? timestampToSeconds(episode.duration) : null episode.durationSeconds = episode.duration ? timestampToSeconds(episode.duration) : null
if (item['psc:chapters']?.[0]?.['psc:chapter']?.length && episodeDuration) {
if (item['psc:chapters']?.[0]?.['psc:chapter']?.length && episode.durationSeconds) {
// Example chapter: // Example chapter:
// {"id":0,"start":0,"end":43.004286,"title":"chapter 1"} // {"id":0,"start":0,"end":43.004286,"title":"chapter 1"}
@ -244,7 +246,7 @@ function extractEpisodeData(item) {
} else { } else {
episode.chapters = cleanedChapters.map((chapter, index) => { episode.chapters = cleanedChapters.map((chapter, index) => {
const nextChapter = cleanedChapters[index + 1] const nextChapter = cleanedChapters[index + 1]
const end = nextChapter ? nextChapter.start : episodeDuration const end = nextChapter ? nextChapter.start : episode.durationSeconds
return { return {
id: chapter.id, id: chapter.id,
title: chapter.title, title: chapter.title,
@ -273,6 +275,7 @@ function cleanEpisodeData(data) {
episode: data.episode || '', episode: data.episode || '',
author: data.author || '', author: data.author || '',
duration: data.duration || '', duration: data.duration || '',
durationSeconds: data.durationSeconds || null,
explicit: data.explicit || '', explicit: data.explicit || '',
publishedAt, publishedAt,
enclosure: data.enclosure, enclosure: data.enclosure,