Update controllers to use new user model

This commit is contained in:
advplyr 2024-08-10 17:15:21 -05:00
parent 202ceb02b5
commit 68ef3a07a7
20 changed files with 396 additions and 304 deletions

View file

@ -11,7 +11,7 @@ module.exports = {
const seriesToFilterOut = {}
books.forEach((libraryItem) => {
// get all book series for item that is not already filtered out
const bookSeries = (libraryItem.media.metadata.series || []).filter(se => !seriesToFilterOut[se.id])
const bookSeries = (libraryItem.media.metadata.series || []).filter((se) => !seriesToFilterOut[se.id])
if (!bookSeries.length) return
bookSeries.forEach((bookSeriesObj) => {
@ -43,11 +43,11 @@ module.exports = {
// Library setting to hide series with only 1 book
if (hideSingleBookSeries) {
seriesItems = seriesItems.filter(se => se.books.length > 1)
seriesItems = seriesItems.filter((se) => se.books.length > 1)
}
return seriesItems.map((series) => {
series.books = naturalSort(series.books).asc(li => li.sequence)
series.books = naturalSort(series.books).asc((li) => li.sequence)
return series
})
},
@ -55,9 +55,7 @@ module.exports = {
collapseBookSeries(libraryItems, filterSeries, hideSingleBookSeries) {
// Get series from the library items. If this list is being collapsed after filtering for a series,
// don't collapse that series, only books that are in other series.
const seriesObjects = this
.getSeriesFromBooks(libraryItems, filterSeries, hideSingleBookSeries)
.filter(s => s.id != filterSeries)
const seriesObjects = this.getSeriesFromBooks(libraryItems, filterSeries, hideSingleBookSeries).filter((s) => s.id != filterSeries)
const filteredLibraryItems = []
@ -65,22 +63,29 @@ module.exports = {
if (li.mediaType != 'book') return
// Handle when this is the first book in a series
seriesObjects.filter(s => s.books[0].id == li.id).forEach(series => {
// Clone the library item as we need to attach data to it, but don't
// want to change the global copy of the library item
filteredLibraryItems.push(Object.assign(
Object.create(Object.getPrototypeOf(li)),
li, { collapsedSeries: series }))
})
seriesObjects
.filter((s) => s.books[0].id == li.id)
.forEach((series) => {
// Clone the library item as we need to attach data to it, but don't
// want to change the global copy of the library item
filteredLibraryItems.push(Object.assign(Object.create(Object.getPrototypeOf(li)), li, { collapsedSeries: series }))
})
// Only included books not contained in series
if (!seriesObjects.some(s => s.books.some(b => b.id == li.id)))
filteredLibraryItems.push(li)
if (!seriesObjects.some((s) => s.books.some((b) => b.id == li.id))) filteredLibraryItems.push(li)
})
return filteredLibraryItems
},
/**
*
* @param {*} payload
* @param {string} seriesId
* @param {import('../models/User')} user
* @param {import('../objects/Library')} library
* @returns {Object[]}
*/
async handleCollapseSubseries(payload, seriesId, user, library) {
const seriesWithBooks = await Database.seriesModel.findByPk(seriesId, {
include: {
@ -112,17 +117,18 @@ module.exports = {
return []
}
const books = seriesWithBooks.books
payload.total = books.length
let libraryItems = books.map((book) => {
const libraryItem = book.libraryItem
libraryItem.media = book
return Database.libraryItemModel.getOldLibraryItem(libraryItem)
}).filter(li => {
return user.checkCanAccessLibraryItem(li)
})
let libraryItems = books
.map((book) => {
const libraryItem = book.libraryItem
libraryItem.media = book
return Database.libraryItemModel.getOldLibraryItem(libraryItem)
})
.filter((li) => {
return user.checkCanAccessLibraryItem(li)
})
const collapsedItems = this.collapseBookSeries(libraryItems, seriesId, library.settings.hideSingleBookSeries)
if (!(collapsedItems.length == 1 && collapsedItems[0].collapsedSeries)) {
@ -139,7 +145,8 @@ module.exports = {
{
[direction]: (li) => li.media.metadata.getSeries(seriesId).sequence
},
{ // If no series sequence then fallback to sorting by title (or collapsed series name for sub-series)
{
// If no series sequence then fallback to sorting by title (or collapsed series name for sub-series)
[direction]: (li) => {
if (sortingIgnorePrefix) {
return li.collapsedSeries?.nameIgnorePrefix || li.media.metadata.titleIgnorePrefix
@ -150,7 +157,7 @@ module.exports = {
}
]
} else {
// If series are collapsed and not sorting by title or sequence,
// If series are collapsed and not sorting by title or sequence,
// sort all collapsed series to the end in alphabetical order
if (payload.sortBy !== 'media.metadata.title') {
sortArray.push({
@ -185,47 +192,48 @@ module.exports = {
libraryItems = libraryItems.slice(startIndex, startIndex + payload.limit)
}
return Promise.all(libraryItems.map(async li => {
const filteredSeries = li.media.metadata.getSeries(seriesId)
const json = li.toJSONMinified()
json.media.metadata.series = {
id: filteredSeries.id,
name: filteredSeries.name,
sequence: filteredSeries.sequence
}
if (li.collapsedSeries) {
json.collapsedSeries = {
id: li.collapsedSeries.id,
name: li.collapsedSeries.name,
nameIgnorePrefix: li.collapsedSeries.nameIgnorePrefix,
libraryItemIds: li.collapsedSeries.books.map(b => b.id),
numBooks: li.collapsedSeries.books.length
return Promise.all(
libraryItems.map(async (li) => {
const filteredSeries = li.media.metadata.getSeries(seriesId)
const json = li.toJSONMinified()
json.media.metadata.series = {
id: filteredSeries.id,
name: filteredSeries.name,
sequence: filteredSeries.sequence
}
// If collapsing by series and filtering by a series, generate the list of sequences the collapsed
// series represents in the filtered series
json.collapsedSeries.seriesSequenceList =
naturalSort(li.collapsedSeries.books.filter(b => b.filterSeriesSequence).map(b => b.filterSeriesSequence)).asc()
if (li.collapsedSeries) {
json.collapsedSeries = {
id: li.collapsedSeries.id,
name: li.collapsedSeries.name,
nameIgnorePrefix: li.collapsedSeries.nameIgnorePrefix,
libraryItemIds: li.collapsedSeries.books.map((b) => b.id),
numBooks: li.collapsedSeries.books.length
}
// If collapsing by series and filtering by a series, generate the list of sequences the collapsed
// series represents in the filtered series
json.collapsedSeries.seriesSequenceList = naturalSort(li.collapsedSeries.books.filter((b) => b.filterSeriesSequence).map((b) => b.filterSeriesSequence))
.asc()
.reduce((ranges, currentSequence) => {
let lastRange = ranges.at(-1)
let isNumber = /^(\d+|\d+\.\d*|\d*\.\d+)$/.test(currentSequence)
if (isNumber) currentSequence = parseFloat(currentSequence)
if (lastRange && isNumber && lastRange.isNumber && ((lastRange.end + 1) == currentSequence)) {
if (lastRange && isNumber && lastRange.isNumber && lastRange.end + 1 == currentSequence) {
lastRange.end = currentSequence
}
else {
} else {
ranges.push({ start: currentSequence, end: currentSequence, isNumber: isNumber })
}
return ranges
}, [])
.map(r => r.start == r.end ? r.start : `${r.start}-${r.end}`)
.map((r) => (r.start == r.end ? r.start : `${r.start}-${r.end}`))
.join(', ')
}
}
return json
}))
return json
})
)
}
}

View file

@ -16,7 +16,7 @@ module.exports = {
/**
* Get library items using filter and sort
* @param {import('../../objects/Library')} library
* @param {import('../../objects/user/User')} user
* @param {import('../../models/User')} user
* @param {object} options
* @returns {object} { libraryItems:LibraryItem[], count:number }
*/
@ -42,7 +42,7 @@ module.exports = {
/**
* Get library items for continue listening & continue reading shelves
* @param {import('../../objects/Library')} library
* @param {import('../../objects/user/User')} user
* @param {import('../../models/User')} user
* @param {string[]} include
* @param {number} limit
* @returns {Promise<{ items:import('../../models/LibraryItem')[], count:number }>}
@ -79,7 +79,7 @@ module.exports = {
/**
* Get library items for most recently added shelf
* @param {import('../../objects/Library')} library
* @param {oldUser} user
* @param {import('../../models/User')} user
* @param {string[]} include
* @param {number} limit
* @returns {object} { libraryItems:LibraryItem[], count:number }
@ -127,7 +127,7 @@ module.exports = {
/**
* Get library items for continue series shelf
* @param {import('../../objects/Library')} library
* @param {oldUser} user
* @param {import('../../models/User')} user
* @param {string[]} include
* @param {number} limit
* @returns {object} { libraryItems:LibraryItem[], count:number }
@ -155,7 +155,7 @@ module.exports = {
/**
* Get library items or podcast episodes for the "Listen Again" and "Read Again" shelf
* @param {import('../../objects/Library')} library
* @param {oldUser} user
* @param {import('../../models/User')} user
* @param {string[]} include
* @param {number} limit
* @returns {object} { items:object[], count:number }
@ -192,7 +192,7 @@ module.exports = {
/**
* Get series for recent series shelf
* @param {import('../../objects/Library')} library
* @param {import('../../objects/user/User')} user
* @param {import('../../models/User')} user
* @param {string[]} include
* @param {number} limit
* @returns {{ series:import('../../objects/entities/Series')[], count:number}}
@ -235,7 +235,7 @@ module.exports = {
if (!user.canAccessExplicitContent) {
attrQuery += ' AND b.explicit = 0'
}
if (!user.permissions.accessAllTags && user.itemTagsSelected.length) {
if (!user.permissions?.accessAllTags && user.permissions?.itemTagsSelected?.length) {
if (user.permissions.selectedTagsNotAccessible) {
attrQuery += ' AND (SELECT count(*) FROM json_each(tags) WHERE json_valid(tags) AND json_each.value IN (:userTagsSelected)) = 0'
} else {
@ -317,7 +317,7 @@ module.exports = {
* Get most recently created authors for "Newest Authors" shelf
* Author must be linked to at least 1 book
* @param {oldLibrary} library
* @param {oldUser} user
* @param {import('../../models/User')} user
* @param {number} limit
* @returns {object} { authors:oldAuthor[], count:number }
*/
@ -360,7 +360,7 @@ module.exports = {
/**
* Get book library items for the "Discover" shelf
* @param {oldLibrary} library
* @param {oldUser} user
* @param {import('../../models/User')} user
* @param {string[]} include
* @param {number} limit
* @returns {object} {libraryItems:oldLibraryItem[], count:number}
@ -387,7 +387,7 @@ module.exports = {
/**
* Get podcast episodes most recently added
* @param {oldLibrary} library
* @param {oldUser} user
* @param {import('../../models/User')} user
* @param {number} limit
* @returns {object} {libraryItems:oldLibraryItem[], count:number}
*/
@ -408,7 +408,7 @@ module.exports = {
/**
* Get library items for an author, optional use user permissions
* @param {oldAuthor} author
* @param {[oldUser]} user
* @param {import('../../models/User')} user
* @param {number} limit
* @param {number} offset
* @returns {Promise<object>} { libraryItems:LibraryItem[], count:number }

View file

@ -6,7 +6,7 @@ const libraryItemsPodcastFilters = require('./libraryItemsPodcastFilters')
module.exports = {
/**
* Get all library items that have tags
* @param {string[]} tags
* @param {string[]} tags
* @returns {Promise<import('../../models/LibraryItem')[]>}
*/
async getAllLibraryItemsWithTags(tags) {
@ -71,7 +71,7 @@ module.exports = {
/**
* Get all library items that have genres
* @param {string[]} genres
* @param {string[]} genres
* @returns {Promise<import('../../models/LibraryItem')[]>}
*/
async getAllLibraryItemsWithGenres(genres) {
@ -131,10 +131,10 @@ module.exports = {
},
/**
* Get all library items that have narrators
* @param {string[]} narrators
* @returns {Promise<import('../../models/LibraryItem')[]>}
*/
* Get all library items that have narrators
* @param {string[]} narrators
* @returns {Promise<import('../../models/LibraryItem')[]>}
*/
async getAllLibraryItemsWithNarrators(narrators) {
const libraryItems = []
const booksWithGenre = await Database.bookModel.findAll({
@ -172,24 +172,24 @@ module.exports = {
/**
* Search library items
* @param {import('../../objects/user/User')} oldUser
* @param {import('../../objects/Library')} oldLibrary
* @param {import('../../models/User')} user
* @param {import('../../objects/Library')} oldLibrary
* @param {string} query
* @param {number} limit
* @param {number} limit
* @returns {{book:object[], narrators:object[], authors:object[], tags:object[], series:object[], podcast:object[]}}
*/
search(oldUser, oldLibrary, query, limit) {
search(user, oldLibrary, query, limit) {
if (oldLibrary.isBook) {
return libraryItemsBookFilters.search(oldUser, oldLibrary, query, limit, 0)
return libraryItemsBookFilters.search(user, oldLibrary, query, limit, 0)
} else {
return libraryItemsPodcastFilters.search(oldUser, oldLibrary, query, limit, 0)
return libraryItemsPodcastFilters.search(user, oldLibrary, query, limit, 0)
}
},
/**
* Get largest items in library
* @param {string} libraryId
* @param {number} limit
* @param {string} libraryId
* @param {number} limit
* @returns {Promise<{ id:string, title:string, size:number }[]>}
*/
async getLargestItems(libraryId, limit) {
@ -208,12 +208,10 @@ module.exports = {
attributes: ['id', 'title']
}
],
order: [
['size', 'DESC']
],
order: [['size', 'DESC']],
limit
})
return libraryItems.map(libraryItem => {
return libraryItems.map((libraryItem) => {
return {
id: libraryItem.id,
title: libraryItem.media.title,
@ -221,4 +219,4 @@ module.exports = {
}
})
}
}
}

View file

@ -8,7 +8,7 @@ const ShareManager = require('../../managers/ShareManager')
module.exports = {
/**
* User permissions to restrict books for explicit content & tags
* @param {import('../../objects/user/User')} user
* @param {import('../../models/User')} user
* @returns {{ bookWhere:Sequelize.WhereOptions, replacements:object }}
*/
getUserPermissionBookWhereQuery(user) {
@ -21,8 +21,8 @@ module.exports = {
explicit: false
})
}
if (!user.permissions.accessAllTags && user.itemTagsSelected.length) {
replacements['userTagsSelected'] = user.itemTagsSelected
if (!user.permissions?.accessAllTags && user.permissions?.itemTagsSelected?.length) {
replacements['userTagsSelected'] = user.permissions.itemTagsSelected
if (user.permissions.selectedTagsNotAccessible) {
bookWhere.push(Sequelize.where(Sequelize.literal(`(SELECT count(*) FROM json_each(tags) WHERE json_valid(tags) AND json_each.value IN (:userTagsSelected))`), 0))
} else {
@ -333,7 +333,7 @@ module.exports = {
/**
* Get library items for book media type using filter and sort
* @param {string} libraryId
* @param {import('../../objects/user/User')} user
* @param {import('../../models/User')} user
* @param {string|null} filterGroup
* @param {string|null} filterValue
* @param {string} sortBy
@ -637,7 +637,7 @@ module.exports = {
* 3. Has at least 1 unfinished book
* TODO: Reduce queries
* @param {import('../../objects/Library')} library
* @param {import('../../objects/user/User')} user
* @param {import('../../models/User')} user
* @param {string[]} include
* @param {number} limit
* @param {number} offset
@ -672,7 +672,7 @@ module.exports = {
where: [
{
id: {
[Sequelize.Op.notIn]: user.seriesHideFromContinueListening
[Sequelize.Op.notIn]: user.extraData?.seriesHideFromContinueListening || []
},
libraryId
},
@ -780,7 +780,7 @@ module.exports = {
* Random selection of books that are not started
* - only includes the first book of a not-started series
* @param {string} libraryId
* @param {oldUser} user
* @param {import('../../models/User')} user
* @param {string[]} include
* @param {number} limit
* @returns {object} {libraryItems:LibraryItem, count:number}
@ -955,25 +955,25 @@ module.exports = {
/**
* Get library items for series
* @param {import('../../objects/entities/Series')} oldSeries
* @param {import('../../objects/user/User')} [oldUser]
* @param {import('../../models/User')} [user]
* @returns {Promise<import('../../objects/LibraryItem')[]>}
*/
async getLibraryItemsForSeries(oldSeries, oldUser) {
const { libraryItems } = await this.getFilteredLibraryItems(oldSeries.libraryId, oldUser, 'series', oldSeries.id, null, null, false, [], null, null)
async getLibraryItemsForSeries(oldSeries, user) {
const { libraryItems } = await this.getFilteredLibraryItems(oldSeries.libraryId, user, 'series', oldSeries.id, null, null, false, [], null, null)
return libraryItems.map((li) => Database.libraryItemModel.getOldLibraryItem(li))
},
/**
* Search books, authors, series
* @param {import('../../objects/user/User')} oldUser
* @param {import('../../models/User')} user
* @param {import('../../objects/Library')} oldLibrary
* @param {string} query
* @param {number} limit
* @param {number} offset
* @returns {{book:object[], narrators:object[], authors:object[], tags:object[], series:object[]}}
*/
async search(oldUser, oldLibrary, query, limit, offset) {
const userPermissionBookWhere = this.getUserPermissionBookWhereQuery(oldUser)
async search(user, oldLibrary, query, limit, offset) {
const userPermissionBookWhere = this.getUserPermissionBookWhereQuery(user)
const normalizedQuery = query

View file

@ -1,12 +1,11 @@
const Sequelize = require('sequelize')
const Database = require('../../Database')
const Logger = require('../../Logger')
const { asciiOnlyToLowerCase } = require('../index')
module.exports = {
/**
* User permissions to restrict podcasts for explicit content & tags
* @param {import('../../objects/user/User')} user
* @param {import('../../models/User')} user
* @returns {{ podcastWhere:Sequelize.WhereOptions, replacements:object }}
*/
getUserPermissionPodcastWhereQuery(user) {
@ -17,18 +16,20 @@ module.exports = {
explicit: false
})
}
if (!user.permissions.accessAllTags && user.itemTagsSelected.length) {
replacements['userTagsSelected'] = user.itemTagsSelected
if (!user.permissions?.accessAllTags && user.permissions?.itemTagsSelected?.length) {
replacements['userTagsSelected'] = user.permissions.itemTagsSelected
if (user.permissions.selectedTagsNotAccessible) {
podcastWhere.push(Sequelize.where(Sequelize.literal(`(SELECT count(*) FROM json_each(tags) WHERE json_valid(tags) AND json_each.value IN (:userTagsSelected))`), 0))
bookWhere.push(Sequelize.where(Sequelize.literal(`(SELECT count(*) FROM json_each(tags) WHERE json_valid(tags) AND json_each.value IN (:userTagsSelected))`), 0))
} else {
podcastWhere.push(
bookWhere.push(
Sequelize.where(Sequelize.literal(`(SELECT count(*) FROM json_each(tags) WHERE json_valid(tags) AND json_each.value IN (:userTagsSelected))`), {
[Sequelize.Op.gte]: 1
})
)
}
}
return {
podcastWhere,
replacements
@ -98,7 +99,7 @@ module.exports = {
/**
* Get library items for podcast media type using filter and sort
* @param {string} libraryId
* @param {oldUser} user
* @param {import('../../models/User')} user
* @param {[string]} filterGroup
* @param {[string]} filterValue
* @param {string} sortBy
@ -200,7 +201,7 @@ module.exports = {
/**
* Get podcast episodes filtered and sorted
* @param {string} libraryId
* @param {oldUser} user
* @param {import('../../models/User')} user
* @param {[string]} filterGroup
* @param {[string]} filterValue
* @param {string} sortBy
@ -304,15 +305,15 @@ module.exports = {
/**
* Search podcasts
* @param {import('../../objects/user/User')} oldUser
* @param {import('../../models/User')} user
* @param {import('../../objects/Library')} oldLibrary
* @param {string} query
* @param {number} limit
* @param {number} offset
* @returns {{podcast:object[], tags:object[]}}
*/
async search(oldUser, oldLibrary, query, limit, offset) {
const userPermissionPodcastWhere = this.getUserPermissionPodcastWhereQuery(oldUser)
async search(user, oldLibrary, query, limit, offset) {
const userPermissionPodcastWhere = this.getUserPermissionPodcastWhereQuery(user)
const normalizedQuery = query
const matchTitle = Database.matchExpression('title', normalizedQuery)
@ -410,14 +411,14 @@ module.exports = {
/**
* Most recent podcast episodes not finished
* @param {import('../../objects/user/User')} oldUser
* @param {import('../../models/User')} user
* @param {import('../../objects/Library')} oldLibrary
* @param {number} limit
* @param {number} offset
* @returns {Promise<object[]>}
*/
async getRecentEpisodes(oldUser, oldLibrary, limit, offset) {
const userPermissionPodcastWhere = this.getUserPermissionPodcastWhereQuery(oldUser)
async getRecentEpisodes(user, oldLibrary, limit, offset) {
const userPermissionPodcastWhere = this.getUserPermissionPodcastWhereQuery(user)
const episodes = await Database.podcastEpisodeModel.findAll({
where: {
@ -441,7 +442,7 @@ module.exports = {
{
model: Database.mediaProgressModel,
where: {
userId: oldUser.id
userId: user.id
},
required: false
}

View file

@ -12,7 +12,7 @@ module.exports = {
* Get series filtered and sorted
*
* @param {import('../../objects/Library')} library
* @param {import('../../objects/user/User')} user
* @param {import('../../models/User')} user
* @param {string} filterBy
* @param {string} sortBy
* @param {boolean} sortDesc
@ -93,7 +93,7 @@ module.exports = {
if (!user.canAccessExplicitContent) {
attrQuery += ' AND b.explicit = 0'
}
if (!user.permissions.accessAllTags && user.itemTagsSelected.length) {
if (!user.permissions?.accessAllTags && user.permissions?.itemTagsSelected?.length) {
if (user.permissions.selectedTagsNotAccessible) {
attrQuery += ' AND (SELECT count(*) FROM json_each(tags) WHERE json_valid(tags) AND json_each.value IN (:userTagsSelected)) = 0'
} else {