diff --git a/client/components/tables/podcast/LazyEpisodesTable.vue b/client/components/tables/podcast/LazyEpisodesTable.vue index f2c6f3428..766be2071 100644 --- a/client/components/tables/podcast/LazyEpisodesTable.vue +++ b/client/components/tables/podcast/LazyEpisodesTable.vue @@ -18,6 +18,9 @@ {{ $strings.ButtonCancel }} + + + @@ -32,11 +35,37 @@ - - - - + + + + + {{ $strings.LabelSeason }} {{ group.season }} + {{ group.episodes.length }} + + + expand_more + + + + + + + + + + + + + + + + + + + + + @@ -65,6 +94,7 @@ export default { episodesCopy: [], filterKey: 'incomplete', sortKey: 'publishedAt', + seasonKey: 'allSeasons', sortDesc: true, selectedEpisode: null, showPodcastRemoveModal: false, @@ -81,7 +111,8 @@ export default { episodeComponentRefs: {}, windowHeight: 0, episodesTableOffsetTop: 0, - episodeRowHeight: 176 + episodeRowHeight: 176, + seasonGroups: {} } }, watch: { @@ -89,6 +120,12 @@ export default { handler() { this.refresh() } + }, + episodesList() { + this.updateSeasonGroups(); + }, + episodesSorted() { + this.updateSeasonGroups(); } }, computed: { @@ -145,6 +182,24 @@ export default { } ] }, + seasonItems() { + const seasonsMap = new Map(); + seasonsMap.set('allSeasons', this.$strings.LabelAllSeasons); + + this.episodesCopy.forEach((episode) => { + const season = episode.season || 'unknown'; + if (!seasonsMap.has(season)) { + seasonsMap.set(season, `${this.$strings.LabelSeason} ${season}`); + } + }); + + if (seasonsMap.has('unknown')) { + seasonsMap.delete('unknown'); + seasonsMap.set('unknown', this.$strings.LabelSeasonUnknown); + } + + return Array.from(seasonsMap, ([value, text]) => ({ value, text })); + }, isSelectionMode() { return this.selectedEpisodes.length > 0 }, @@ -157,13 +212,21 @@ export default { mediaMetadata() { return this.media.metadata || {} }, + podcastType() { + return this.mediaMetadata.type || 'episodic' + }, episodes() { return this.media.episodes || [] }, episodesSorted() { return this.episodesCopy .filter((ep) => { - if (this.filterKey === 'all') return true + // Filter by season + const season = ep.season && ep.season.trim() !== '' ? ep.season : 'unknown'; + if (this.podcastType === 'serial' && this.seasonKey === 'unknown' && season !== 'unknown') return false; + if (this.podcastType === 'serial' && this.seasonKey !== 'allSeasons' && season !== this.seasonKey) return false; + + if (this.filterKey === 'all') return true; const episodeProgress = this.$store.getters['user/getUserMediaProgress'](this.libraryItem.id, ep.id) if (this.filterKey === 'incomplete') return !episodeProgress || !episodeProgress.isFinished if (this.filterKey === 'complete') return episodeProgress && episodeProgress.isFinished @@ -209,9 +272,42 @@ export default { }, timeFormat() { return this.$store.state.serverSettings.timeFormat - } + }, }, methods: { + updateSeasonGroups() { + if (!this.episodesSorted.length) { + this.seasonGroups = {}; + return; + } + + const grouped = this.episodesSorted.reduce((grouped, episode, index) => { + let season = Number(episode.season); + season = Number.isInteger(season) ? season : 'unknown'; + const seasonGroup = grouped[season]; + + if (!seasonGroup) { + const newGroup = { + season, + episodes: [index], + showSeason: false + }; + grouped[season] = newGroup; + } else { + seasonGroup.episodes.push(index); + } + + return grouped; + }, {}); + + const sortedGrouped = Object.values(grouped).sort((a, b) => b.season - a.season); + + if (sortedGrouped.length > 0) { + sortedGrouped[0].showSeason = true; + } + + this.seasonGroups = sortedGrouped; + }, submit() {}, inputUpdate() { clearTimeout(this.searchTimeout) @@ -409,6 +505,9 @@ export default { this.episodeComponentRefs = {} this.episodeIndexesMounted = [] }, + clickBar(group) { + group.showSeason = !group.showSeason + }, mountEpisode(index) { const episodeEl = document.getElementById(`episode-${index}`) if (!episodeEl) { diff --git a/client/strings/en-us.json b/client/strings/en-us.json index a7b3864bb..8333ae47c 100644 --- a/client/strings/en-us.json +++ b/client/strings/en-us.json @@ -202,6 +202,7 @@ "LabelAddToPlaylistBatch": "Add {0} Items to Playlist", "LabelAdminUsersOnly": "Admin users only", "LabelAll": "All", + "LabelAllSeasons": "All Seasons", "LabelAllUsers": "All Users", "LabelAllUsersExcludingGuests": "All users excluding guests", "LabelAllUsersIncludingGuests": "All users including guests", @@ -439,6 +440,7 @@ "LabelSearchTitle": "Search Title", "LabelSearchTitleOrASIN": "Search Title or ASIN", "LabelSeason": "Season", + "LabelSeasonUnknown": "Season Unknown", "LabelSelectAllEpisodes": "Select all episodes", "LabelSelectEpisodesShowing": "Select {0} episodes showing", "LabelSelectUsers": "Select users",
{{ $strings.LabelSeason }} {{ group.season }}