diff --git a/client/components/app/BookShelfToolbar.vue b/client/components/app/BookShelfToolbar.vue
index ad1fe226..bbf30f6e 100644
--- a/client/components/app/BookShelfToolbar.vue
+++ b/client/components/app/BookShelfToolbar.vue
@@ -43,8 +43,9 @@
-
-
+
+
+
Remove All {{ numShowing }} {{ entityName }}
@@ -183,6 +184,14 @@ export default {
set(val) {
this.$store.commit('libraries/setSeriesSortDesc', val)
}
+ },
+ seriesFilterBy: {
+ get() {
+ return this.$store.state.libraries.seriesFilterBy
+ },
+ set(val) {
+ this.$store.commit('libraries/setSeriesFilterBy', val)
+ }
}
},
methods: {
@@ -265,6 +274,9 @@ export default {
updateSeriesSort() {
this.$eventBus.$emit('series-sort-updated')
},
+ updateSeriesFilter() {
+ this.$eventBus.$emit('series-sort-updated')
+ },
updateCollapseSeries() {
this.saveSettings()
},
diff --git a/client/components/app/LazyBookshelf.vue b/client/components/app/LazyBookshelf.vue
index b863f5e1..08ed1496 100644
--- a/client/components/app/LazyBookshelf.vue
+++ b/client/components/app/LazyBookshelf.vue
@@ -104,6 +104,9 @@ export default {
seriesSortDesc() {
return this.$store.state.libraries.seriesSortDesc
},
+ seriesFilterBy() {
+ return this.$store.state.libraries.seriesFilterBy
+ },
orderBy() {
return this.$store.getters['user/getUserSetting']('orderBy')
},
@@ -431,6 +434,7 @@ export default {
if (this.page === 'series') {
searchParams.set('sort', this.seriesSortBy)
searchParams.set('desc', this.seriesSortDesc ? 1 : 0)
+ searchParams.set('filter', this.seriesFilterBy)
} else if (this.page === 'series-books') {
searchParams.set('filter', `series.${this.$encode(this.seriesId)}`)
} else {
diff --git a/client/components/controls/EpisodeFilterSelect.vue b/client/components/controls/EpisodeFilterSelect.vue
deleted file mode 100644
index 6fcbc116..00000000
--- a/client/components/controls/EpisodeFilterSelect.vue
+++ /dev/null
@@ -1,100 +0,0 @@
-
-
-
-
- {{ selectedText }}
-
-
-
-
-
-
-
- close
-
-
-
-
-
-
-
-
- {{ item.text }}
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/client/components/controls/FilterSelect.vue b/client/components/controls/FilterSelect.vue
index 3a92f9a1..1cb127f6 100644
--- a/client/components/controls/FilterSelect.vue
+++ b/client/components/controls/FilterSelect.vue
@@ -1,8 +1,8 @@
-
+
- {{ selectedText }}
+ {{ selectedText }}
@@ -15,42 +15,12 @@
-
-
+
+
{{ item.text }}
-
- arrow_right
-
-
-
-
-
-
-
- arrow_left
-
-
- Back
-
-
-
-
- No {{ sublist }}
-
-
-
-
- No Series
-
-
-
-
-
- {{ item.text }}
-
@@ -61,97 +31,15 @@
\ No newline at end of file
diff --git a/client/components/controls/OrderSelect.vue b/client/components/controls/LibrarySortSelect.vue
similarity index 100%
rename from client/components/controls/OrderSelect.vue
rename to client/components/controls/LibrarySortSelect.vue
diff --git a/client/components/tables/podcast/EpisodesTable.vue b/client/components/tables/podcast/EpisodesTable.vue
index 3d752b93..d35b5f1b 100644
--- a/client/components/tables/podcast/EpisodesTable.vue
+++ b/client/components/tables/podcast/EpisodesTable.vue
@@ -11,8 +11,8 @@
Cancel
-
-
+
+
No Episodes
@@ -60,6 +60,24 @@ export default {
text: 'Episode',
value: 'episode'
}
+ ],
+ filterItems: [
+ {
+ value: 'all',
+ text: 'Show All'
+ },
+ {
+ value: 'incomplete',
+ text: 'Incomplete'
+ },
+ {
+ value: 'complete',
+ text: 'Complete'
+ },
+ {
+ value: 'in_progress',
+ text: 'In Progress'
+ }
]
}
},
diff --git a/client/middleware/authenticated.js b/client/middleware/authenticated.js
index d1af3263..dc4f03ca 100644
--- a/client/middleware/authenticated.js
+++ b/client/middleware/authenticated.js
@@ -4,6 +4,6 @@ export default function ({ store, redirect, route, app }) {
if (route.name === 'batch' || route.name === 'index') {
return redirect('/login')
}
- return redirect(`/login?redirect=${route.fullPath}`)
+ return redirect(`/login?redirect=${encodeURIComponent(route.fullPath)}`)
}
}
\ No newline at end of file
diff --git a/client/pages/library/_library/bookshelf/_id.vue b/client/pages/library/_library/bookshelf/_id.vue
index 5ed86eae..8cc77e83 100644
--- a/client/pages/library/_library/bookshelf/_id.vue
+++ b/client/pages/library/_library/bookshelf/_id.vue
@@ -15,12 +15,17 @@ export default {
}
// Set series sort by
- if (query.sort && params.id === 'series') {
- store.commit('libraries/setSeriesSortBy', query.sort)
- store.commit('libraries/setSeriesSortDesc', !!query.desc)
- }
- // Set filter by
- if (query.filter && params.id !== 'series') {
+ if (params.id === 'series') {
+ console.log('Series page', query)
+ if (query.sort) {
+ store.commit('libraries/setSeriesSortBy', query.sort)
+ store.commit('libraries/setSeriesSortDesc', !!query.desc)
+ }
+ if (query.filter) {
+ console.log('has filter', query.filter)
+ store.commit('libraries/setSeriesFilterBy', query.filter)
+ }
+ } else if (query.filter) {
store.dispatch('user/updateUserSettings', { filterBy: query.filter })
}
diff --git a/client/store/libraries.js b/client/store/libraries.js
index fcc986b6..a7538b48 100644
--- a/client/store/libraries.js
+++ b/client/store/libraries.js
@@ -10,7 +10,8 @@ export const state = () => ({
folderLastUpdate: 0,
filterData: null,
seriesSortBy: 'name',
- seriesSortDesc: false
+ seriesSortDesc: false,
+ seriesFilterBy: 'all'
})
export const getters = {
@@ -297,5 +298,8 @@ export const mutations = {
},
setSeriesSortDesc(state, sortDesc) {
state.seriesSortDesc = sortDesc
+ },
+ setSeriesFilterBy(state, filterBy) {
+ state.seriesFilterBy = filterBy
}
}
\ No newline at end of file
diff --git a/server/controllers/LibraryController.js b/server/controllers/LibraryController.js
index 199a532b..1988b9f7 100644
--- a/server/controllers/LibraryController.js
+++ b/server/controllers/LibraryController.js
@@ -275,7 +275,8 @@ class LibraryController {
minified: req.query.minified === '1'
}
- var series = libraryHelpers.getSeriesFromBooks(libraryItems, this.db.series, payload.minified)
+ var series = libraryHelpers.getSeriesFromBooks(libraryItems, this.db.series, payload.filterBy, req.user, payload.minified)
+
const direction = payload.sortDesc ? 'desc' : 'asc'
series = naturalSort(series).by([
{
diff --git a/server/utils/libraryHelpers.js b/server/utils/libraryHelpers.js
index 70b22537..21a13683 100644
--- a/server/utils/libraryHelpers.js
+++ b/server/utils/libraryHelpers.js
@@ -67,6 +67,45 @@ module.exports = {
return filtered
},
+ // Returns false if should be filtered out
+ checkFilterForSeriesLibraryItem(libraryItem, filterBy) {
+ var searchGroups = ['genres', 'tags', 'authors', 'progress', 'narrators', 'languages']
+ var group = searchGroups.find(_group => filterBy.startsWith(_group + '.'))
+ if (group) {
+ var filterVal = filterBy.replace(`${group}.`, '')
+ var filter = this.decode(filterVal)
+
+ if (group === 'genres') return libraryItem.media.metadata && libraryItem.media.metadata.genres.includes(filter)
+ else if (group === 'tags') return libraryItem.media.tags.includes(filter)
+ else if (group === 'authors') return libraryItem.mediaType === 'book' && libraryItem.media.metadata.hasAuthor(filter)
+ else if (group === 'narrators') return libraryItem.mediaType === 'book' && libraryItem.media.metadata.hasNarrator(filter)
+ else if (group === 'languages') {
+ return libraryItem.media.metadata && libraryItem.media.metadata.language === filter
+ }
+ }
+ return true
+ },
+
+ // Return false to filter out series
+ checkSeriesProgressFilter(series, filterBy, user) {
+ const filter = this.decode(filterBy.split('.')[1])
+
+ var numBooksStartedOrFinished = 0
+ for (const libraryItem of series.books) {
+ const itemProgress = user.getMediaProgress(libraryItem.id)
+ if (filter === 'Finished' && (!itemProgress || !itemProgress.isFinished)) return false
+ if (filter === 'Not Started' && itemProgress) return false
+ if (itemProgress) numBooksStartedOrFinished++
+ }
+
+ if (numBooksStartedOrFinished === series.books.length) { // Completely finished series
+ if (filter === 'Not Finished') return false
+ } else if (numBooksStartedOrFinished === 0 && filter === 'In Progress') { // Series not started
+ return false
+ }
+ return true
+ },
+
getDistinctFilterDataNew(libraryItems) {
var data = {
authors: [],
@@ -114,10 +153,27 @@ module.exports = {
return data
},
- getSeriesFromBooks(books, allSeries, minified = false) {
+ getSeriesFromBooks(books, allSeries, filterBy, user, minified = false) {
const _series = {}
+ const seriesToFilterOut = {}
books.forEach((libraryItem) => {
- const bookSeries = libraryItem.media.metadata.series || []
+ // get all book series for item that is not already filtered out
+ const bookSeries = (libraryItem.media.metadata.series || []).filter(se => !seriesToFilterOut[se.id])
+ if (!bookSeries.length) return
+
+ if (filterBy && user && !filterBy.startsWith('progress.')) { // Series progress filters are evaluated after grouping
+ // If a single book in a series is filtered out then filter out the entire series
+ if (!this.checkFilterForSeriesLibraryItem(libraryItem, filterBy)) {
+ // filter out this library item
+ bookSeries.forEach((bookSeriesObj) => {
+ // flag series to filter it out
+ seriesToFilterOut[bookSeriesObj.id] = true
+ delete _series[bookSeriesObj.id]
+ })
+ return
+ }
+ }
+
bookSeries.forEach((bookSeriesObj) => {
const series = allSeries.find(se => se.id === bookSeriesObj.id)
@@ -140,7 +196,15 @@ module.exports = {
}
})
})
- return Object.values(_series).map((series) => {
+
+ var seriesItems = Object.values(_series)
+
+ // check progress filter
+ if (filterBy && filterBy.startsWith('progress.') && user) {
+ seriesItems = seriesItems.filter(se => this.checkSeriesProgressFilter(se, filterBy, user))
+ }
+
+ return seriesItems.map((series) => {
series.books = naturalSort(series.books).asc(li => li.sequence)
return series
})
@@ -216,7 +280,7 @@ module.exports = {
},
collapseBookSeries(libraryItems, series) {
- var seriesObjects = this.getSeriesFromBooks(libraryItems, series, true)
+ var seriesObjects = this.getSeriesFromBooks(libraryItems, series, null, null, true)
var seriesToUse = {}
var libraryItemIdsToHide = []
seriesObjects.forEach((series) => {