diff --git a/client/components/app/BookShelfToolbar.vue b/client/components/app/BookShelfToolbar.vue
index 5a869f7f60..94e095c7c2 100644
--- a/client/components/app/BookShelfToolbar.vue
+++ b/client/components/app/BookShelfToolbar.vue
@@ -84,11 +84,6 @@
-
-
-
-
-
@@ -99,10 +94,15 @@
- {{ $strings.ButtonMatchAllAuthors }}
+ {{ $strings.ButtonMatchAllAuthors }}
-
+
+
+
+
+
+
@@ -187,6 +187,10 @@ export default {
{
text: this.$strings.LabelTotalDuration,
value: 'totalDuration'
+ },
+ {
+ text: this.$strings.LabelRandomly,
+ value: 'random'
}
]
},
diff --git a/client/components/controls/LibrarySortSelect.vue b/client/components/controls/LibrarySortSelect.vue
index f0cc095560..afda848079 100644
--- a/client/components/controls/LibrarySortSelect.vue
+++ b/client/components/controls/LibrarySortSelect.vue
@@ -88,6 +88,10 @@ export default {
{
text: this.$strings.LabelFileModified,
value: 'mtimeMs'
+ },
+ {
+ text: this.$strings.LabelRandomly,
+ value: 'random'
}
]
},
@@ -128,6 +132,10 @@ export default {
{
text: this.$strings.LabelFileModified,
value: 'mtimeMs'
+ },
+ {
+ text: this.$strings.LabelRandomly,
+ value: 'random'
}
]
},
@@ -215,4 +223,4 @@ export default {
}
}
}
-
\ No newline at end of file
+
diff --git a/client/strings/en-us.json b/client/strings/en-us.json
index 7fecd71ce2..111f9ef95f 100644
--- a/client/strings/en-us.json
+++ b/client/strings/en-us.json
@@ -455,6 +455,7 @@
"LabelRSSFeedPreventIndexing": "Prevent Indexing",
"LabelRSSFeedSlug": "RSS Feed Slug",
"LabelRSSFeedURL": "RSS Feed URL",
+ "LabelRandomly": "Randomly",
"LabelReAddSeriesToContinueListening": "Re-add series to Continue Listening",
"LabelRead": "Read",
"LabelReadAgain": "Read Again",
diff --git a/server/utils/queries/libraryItemsBookFilters.js b/server/utils/queries/libraryItemsBookFilters.js
index 0af86730a8..0be5f154e1 100644
--- a/server/utils/queries/libraryItemsBookFilters.js
+++ b/server/utils/queries/libraryItemsBookFilters.js
@@ -2,7 +2,6 @@ const Sequelize = require('sequelize')
const Database = require('../../Database')
const Logger = require('../../Logger')
const authorFilters = require('./authorFilters')
-const { asciiOnlyToLowerCase } = require('../index')
const ShareManager = require('../../managers/ShareManager')
@@ -274,6 +273,8 @@ module.exports = {
return [[Sequelize.literal(`CAST(\`series.bookSeries.sequence\` AS FLOAT) ${nullDir}`)]]
} else if (sortBy === 'progress') {
return [[Sequelize.literal('mediaProgresses.updatedAt'), dir]]
+ } else if (sortBy === 'random') {
+ return [Database.sequelize.random()]
}
return []
},
diff --git a/server/utils/queries/libraryItemsPodcastFilters.js b/server/utils/queries/libraryItemsPodcastFilters.js
index 545ca8a6e5..814ea093e1 100644
--- a/server/utils/queries/libraryItemsPodcastFilters.js
+++ b/server/utils/queries/libraryItemsPodcastFilters.js
@@ -89,6 +89,8 @@ module.exports = {
}
} else if (sortBy === 'media.numTracks') {
return [['numEpisodes', dir]]
+ } else if (sortBy === 'random') {
+ return [Database.sequelize.random()]
}
return []
},
diff --git a/server/utils/queries/seriesFilters.js b/server/utils/queries/seriesFilters.js
index 69e4df0632..1c384085b4 100644
--- a/server/utils/queries/seriesFilters.js
+++ b/server/utils/queries/seriesFilters.js
@@ -10,15 +10,15 @@ module.exports = {
/**
* Get series filtered and sorted
- *
- * @param {import('../../objects/Library')} library
- * @param {import('../../objects/user/User')} user
- * @param {string} filterBy
- * @param {string} sortBy
- * @param {boolean} sortDesc
- * @param {string[]} include
- * @param {number} limit
- * @param {number} offset
+ *
+ * @param {import('../../objects/Library')} library
+ * @param {import('../../objects/user/User')} user
+ * @param {string} filterBy
+ * @param {string} sortBy
+ * @param {boolean} sortDesc
+ * @param {string[]} include
+ * @param {number} limit
+ * @param {number} offset
* @returns {Promise<{ series:object[], count:number }>}
*/
async getFilteredSeries(library, user, filterBy, sortBy, sortDesc, include, limit, offset) {
@@ -26,7 +26,7 @@ module.exports = {
let filterGroup = null
if (filterBy) {
const searchGroups = ['genres', 'tags', 'authors', 'progress', 'narrators', 'publishers', 'languages']
- const group = searchGroups.find(_group => filterBy.startsWith(_group + '.'))
+ const group = searchGroups.find((_group) => filterBy.startsWith(_group + '.'))
filterGroup = group || filterBy
filterValue = group ? this.decode(filterBy.replace(`${group}.`, '')) : null
}
@@ -49,9 +49,11 @@ module.exports = {
// Handle library setting to hide single book series
// TODO: Merge with existing query
if (library.settings.hideSingleBookSeries) {
- seriesWhere.push(Sequelize.where(Sequelize.literal(`(SELECT count(*) FROM books b, bookSeries bs WHERE bs.seriesId = series.id AND bs.bookId = b.id)`), {
- [Sequelize.Op.gt]: 1
- }))
+ seriesWhere.push(
+ Sequelize.where(Sequelize.literal(`(SELECT count(*) FROM books b, bookSeries bs WHERE bs.seriesId = series.id AND bs.bookId = b.id)`), {
+ [Sequelize.Op.gt]: 1
+ })
+ )
}
// Handle filters
@@ -101,9 +103,11 @@ module.exports = {
}
if (attrQuery) {
- seriesWhere.push(Sequelize.where(Sequelize.literal(`(${attrQuery})`), {
- [Sequelize.Op.gt]: 0
- }))
+ seriesWhere.push(
+ Sequelize.where(Sequelize.literal(`(${attrQuery})`), {
+ [Sequelize.Op.gt]: 0
+ })
+ )
}
const order = []
@@ -133,6 +137,8 @@ module.exports = {
} else if (sortBy === 'lastBookUpdated') {
seriesAttributes.include.push([Sequelize.literal('(SELECT MAX(b.updatedAt) FROM books b, bookSeries bs WHERE bs.seriesId = series.id AND b.id = bs.bookId)'), 'mostRecentBookUpdated'])
order.push(['mostRecentBookUpdated', dir])
+ } else if (sortBy === 'random') {
+ order.push(Database.sequelize.random())
}
const { rows: series, count } = await Database.seriesModel.findAndCountAll({
@@ -184,7 +190,7 @@ module.exports = {
sensitivity: 'base'
})
})
- oldSeries.books = s.bookSeries.map(bs => {
+ oldSeries.books = s.bookSeries.map((bs) => {
const libraryItem = bs.book.libraryItem.toJSON()
delete bs.book.libraryItem
libraryItem.media = bs.book