mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-07-11 11:51:36 +00:00
Merge branch 'refs/heads/master' into mf/rssInboundManager
This commit is contained in:
commit
50ea58aea2
193 changed files with 11020 additions and 1005 deletions
|
|
@ -3,45 +3,61 @@ const Database = require('../../Database')
|
|||
|
||||
module.exports = {
|
||||
/**
|
||||
* Get authors with count of num books
|
||||
* @param {string} libraryId
|
||||
* @returns {{id:string, name:string, count:number}}
|
||||
* Get authors total count
|
||||
*
|
||||
* @param {string} libraryId
|
||||
* @returns {Promise<number>} count
|
||||
*/
|
||||
async getAuthorsWithCount(libraryId) {
|
||||
const authors = await Database.authorModel.findAll({
|
||||
where: [
|
||||
{
|
||||
libraryId
|
||||
},
|
||||
Sequelize.where(Sequelize.literal('count'), {
|
||||
[Sequelize.Op.gt]: 0
|
||||
})
|
||||
],
|
||||
attributes: [
|
||||
'id',
|
||||
'name',
|
||||
[Sequelize.literal('(SELECT count(*) FROM bookAuthors ba WHERE ba.authorId = author.id)'), 'count']
|
||||
],
|
||||
order: [
|
||||
['count', 'DESC']
|
||||
]
|
||||
async getAuthorsTotalCount(libraryId) {
|
||||
const authorsCount = await Database.authorModel.count({
|
||||
where: {
|
||||
libraryId: libraryId
|
||||
}
|
||||
})
|
||||
return authors.map(au => {
|
||||
return authorsCount
|
||||
},
|
||||
|
||||
/**
|
||||
* Get authors with count of num books
|
||||
*
|
||||
* @param {string} libraryId
|
||||
* @param {number} limit
|
||||
* @returns {Promise<{id:string, name:string, count:number}>}
|
||||
*/
|
||||
async getAuthorsWithCount(libraryId, limit) {
|
||||
const authors = await Database.bookAuthorModel.findAll({
|
||||
include: [
|
||||
{
|
||||
model: Database.authorModel,
|
||||
as: 'author', // Use the correct alias as defined in your associations
|
||||
attributes: ['name'],
|
||||
where: {
|
||||
libraryId: libraryId
|
||||
}
|
||||
}
|
||||
],
|
||||
attributes: ['authorId', [Sequelize.fn('COUNT', Sequelize.col('authorId')), 'count']],
|
||||
group: ['authorId', 'author.id'], // Include 'author.id' to satisfy GROUP BY with JOIN
|
||||
order: [[Sequelize.literal('count'), 'DESC']],
|
||||
limit: limit
|
||||
})
|
||||
return authors.map((au) => {
|
||||
return {
|
||||
id: au.id,
|
||||
name: au.name,
|
||||
count: au.dataValues.count
|
||||
id: au.authorId,
|
||||
name: au.author.name,
|
||||
count: au.get('count') // Use get method to access aliased attributes
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Search authors
|
||||
* @param {string} libraryId
|
||||
* @param {string} query
|
||||
*
|
||||
* @param {string} libraryId
|
||||
* @param {string} query
|
||||
* @param {number} limit
|
||||
* @param {number} offset
|
||||
* @returns {object[]} oldAuthor with numBooks
|
||||
* @returns {Promise<Object[]>} oldAuthor with numBooks
|
||||
*/
|
||||
async search(libraryId, query, limit, offset) {
|
||||
const authors = await Database.authorModel.findAll({
|
||||
|
|
@ -52,9 +68,7 @@ module.exports = {
|
|||
libraryId
|
||||
},
|
||||
attributes: {
|
||||
include: [
|
||||
[Sequelize.literal('(SELECT count(*) FROM bookAuthors ba WHERE ba.authorId = author.id)'), 'numBooks']
|
||||
]
|
||||
include: [[Sequelize.literal('(SELECT count(*) FROM bookAuthors ba WHERE ba.authorId = author.id)'), 'numBooks']]
|
||||
},
|
||||
limit,
|
||||
offset
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ module.exports = {
|
|||
|
||||
/**
|
||||
* Get library items for most recently added shelf
|
||||
* @param {oldLibrary} library
|
||||
* @param {import('../../objects/Library')} library
|
||||
* @param {oldUser} user
|
||||
* @param {string[]} include
|
||||
* @param {number} limit
|
||||
|
|
@ -120,14 +120,14 @@ module.exports = {
|
|||
|
||||
/**
|
||||
* Get library items for continue series shelf
|
||||
* @param {string} library
|
||||
* @param {import('../../objects/Library')} library
|
||||
* @param {oldUser} user
|
||||
* @param {string[]} include
|
||||
* @param {number} limit
|
||||
* @returns {object} { libraryItems:LibraryItem[], count:number }
|
||||
*/
|
||||
async getLibraryItemsContinueSeries(library, user, include, limit) {
|
||||
const { libraryItems, count } = await libraryItemsBookFilters.getContinueSeriesLibraryItems(library.id, user, include, limit, 0)
|
||||
const { libraryItems, count } = await libraryItemsBookFilters.getContinueSeriesLibraryItems(library, user, include, limit, 0)
|
||||
return {
|
||||
libraryItems: libraryItems.map(li => {
|
||||
const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(li).toJSONMinified()
|
||||
|
|
@ -145,7 +145,7 @@ module.exports = {
|
|||
|
||||
/**
|
||||
* Get library items or podcast episodes for the "Listen Again" and "Read Again" shelf
|
||||
* @param {oldLibrary} library
|
||||
* @param {import('../../objects/Library')} library
|
||||
* @param {oldUser} user
|
||||
* @param {string[]} include
|
||||
* @param {number} limit
|
||||
|
|
@ -451,7 +451,7 @@ module.exports = {
|
|||
libraryId: libraryId
|
||||
}
|
||||
},
|
||||
attributes: ['tags', 'genres']
|
||||
attributes: ['tags', 'genres', 'language']
|
||||
})
|
||||
for (const podcast of podcasts) {
|
||||
if (podcast.tags?.length) {
|
||||
|
|
@ -460,6 +460,9 @@ module.exports = {
|
|||
if (podcast.genres?.length) {
|
||||
podcast.genres.forEach((genre) => data.genres.add(genre))
|
||||
}
|
||||
if (podcast.language) {
|
||||
data.languages.add(podcast.language)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const books = await Database.bookModel.findAll({
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ module.exports = {
|
|||
attributes: ['sequence']
|
||||
}
|
||||
}
|
||||
],
|
||||
order: [
|
||||
[Database.authorModel, Database.bookAuthorModel, 'createdAt', 'ASC'],
|
||||
[Database.seriesModel, 'bookSeries', 'createdAt', 'ASC']
|
||||
]
|
||||
})
|
||||
for (const book of booksWithTag) {
|
||||
|
|
@ -68,7 +72,7 @@ module.exports = {
|
|||
/**
|
||||
* Get all library items that have genres
|
||||
* @param {string[]} genres
|
||||
* @returns {Promise<LibraryItem[]>}
|
||||
* @returns {Promise<import('../../models/LibraryItem')[]>}
|
||||
*/
|
||||
async getAllLibraryItemsWithGenres(genres) {
|
||||
const libraryItems = []
|
||||
|
|
|
|||
|
|
@ -204,6 +204,10 @@ module.exports = {
|
|||
mediaWhere['ebookFile'] = {
|
||||
[Sequelize.Op.not]: null
|
||||
}
|
||||
} else if (value == 'no-ebook') {
|
||||
mediaWhere['ebookFile'] = {
|
||||
[Sequelize.Op.eq]: null
|
||||
}
|
||||
}
|
||||
} else if (group === 'missing') {
|
||||
if (['asin', 'isbn', 'subtitle', 'publishedYear', 'description', 'publisher', 'language', 'cover'].includes(value)) {
|
||||
|
|
@ -421,6 +425,10 @@ module.exports = {
|
|||
libraryItemWhere['libraryFiles'] = {
|
||||
[Sequelize.Op.substring]: `"isSupplementary":true`
|
||||
}
|
||||
} else if (filterGroup === 'ebooks' && filterValue === 'no-supplementary') {
|
||||
libraryItemWhere['libraryFiles'] = {
|
||||
[Sequelize.Op.notLike]: Sequelize.literal(`\'%"isSupplementary":true%\'`),
|
||||
}
|
||||
} else if (filterGroup === 'missing' && filterValue === 'authors') {
|
||||
authorInclude = {
|
||||
model: Database.authorModel,
|
||||
|
|
@ -625,14 +633,15 @@ module.exports = {
|
|||
* 2. Has no books in progress
|
||||
* 3. Has at least 1 unfinished book
|
||||
* TODO: Reduce queries
|
||||
* @param {string} libraryId
|
||||
* @param {oldUser} user
|
||||
* @param {import('../../objects/Library')} library
|
||||
* @param {import('../../objects/user/User')} user
|
||||
* @param {string[]} include
|
||||
* @param {number} limit
|
||||
* @param {number} offset
|
||||
* @returns {object} { libraryItems:LibraryItem[], count:number }
|
||||
* @returns {{ libraryItems:import('../../models/LibraryItem')[], count:number }}
|
||||
*/
|
||||
async getContinueSeriesLibraryItems(libraryId, user, include, limit, offset) {
|
||||
async getContinueSeriesLibraryItems(library, user, include, limit, offset) {
|
||||
const libraryId = library.id
|
||||
const libraryItemIncludes = []
|
||||
if (include.includes('rssfeed')) {
|
||||
libraryItemIncludes.push({
|
||||
|
|
@ -646,6 +655,18 @@ module.exports = {
|
|||
const userPermissionBookWhere = this.getUserPermissionBookWhereQuery(user)
|
||||
bookWhere.push(...userPermissionBookWhere.bookWhere)
|
||||
|
||||
let includeAttributes = [
|
||||
[Sequelize.literal('(SELECT max(mp.updatedAt) FROM bookSeries bs, mediaProgresses mp WHERE mp.mediaItemId = bs.bookId AND mp.userId = :userId AND bs.seriesId = series.id)'), 'recent_progress'],
|
||||
]
|
||||
let booksNotFinishedQuery = `SELECT count(*) FROM bookSeries bs LEFT OUTER JOIN mediaProgresses mp ON mp.mediaItemId = bs.bookId AND mp.userId = :userId WHERE bs.seriesId = series.id AND (mp.isFinished = 0 OR mp.isFinished IS NULL)`
|
||||
|
||||
if (library.settings.onlyShowLaterBooksInContinueSeries) {
|
||||
const maxSequenceQuery = `(SELECT CAST(max(bs.sequence) as FLOAT) FROM bookSeries bs, mediaProgresses mp WHERE mp.mediaItemId = bs.bookId AND mp.isFinished = 1 AND mp.userId = :userId AND bs.seriesId = series.id)`
|
||||
includeAttributes.push([Sequelize.literal(`${maxSequenceQuery}`), 'maxSequence'])
|
||||
|
||||
booksNotFinishedQuery = booksNotFinishedQuery + ` AND CAST(bs.sequence as FLOAT) > ${maxSequenceQuery}`
|
||||
}
|
||||
|
||||
const { rows: series, count } = await Database.seriesModel.findAndCountAll({
|
||||
where: [
|
||||
{
|
||||
|
|
@ -659,17 +680,15 @@ module.exports = {
|
|||
Sequelize.where(Sequelize.literal(`(SELECT count(*) FROM mediaProgresses mp, bookSeries bs WHERE bs.seriesId = series.id AND mp.mediaItemId = bs.bookId AND mp.userId = :userId AND mp.isFinished = 1)`), {
|
||||
[Sequelize.Op.gte]: 1
|
||||
}),
|
||||
// Has at least 1 book not finished
|
||||
Sequelize.where(Sequelize.literal(`(SELECT count(*) FROM bookSeries bs LEFT OUTER JOIN mediaProgresses mp ON mp.mediaItemId = bs.bookId AND mp.userId = :userId WHERE bs.seriesId = series.id AND (mp.isFinished = 0 OR mp.isFinished IS NULL))`), {
|
||||
// Has at least 1 book not finished (that has a sequence number higher than the highest already read, if library config is toggled)
|
||||
Sequelize.where(Sequelize.literal(`(${booksNotFinishedQuery})`), {
|
||||
[Sequelize.Op.gte]: 1
|
||||
}),
|
||||
// Has no books in progress
|
||||
Sequelize.where(Sequelize.literal(`(SELECT count(*) FROM mediaProgresses mp, bookSeries bs WHERE mp.mediaItemId = bs.bookId AND mp.userId = :userId AND bs.seriesId = series.id AND mp.isFinished = 0 AND mp.currentTime > 0)`), 0)
|
||||
],
|
||||
attributes: {
|
||||
include: [
|
||||
[Sequelize.literal('(SELECT max(mp.updatedAt) FROM bookSeries bs, mediaProgresses mp WHERE mp.mediaItemId = bs.bookId AND mp.userId = :userId AND bs.seriesId = series.id)'), 'recent_progress']
|
||||
]
|
||||
include: includeAttributes
|
||||
},
|
||||
replacements: {
|
||||
userId: user.id,
|
||||
|
|
@ -723,13 +742,26 @@ module.exports = {
|
|||
|
||||
const libraryItems = series.map(s => {
|
||||
if (!s.bookSeries.length) return null // this is only possible if user has restricted books in series
|
||||
const libraryItem = s.bookSeries[0].book.libraryItem.toJSON()
|
||||
const book = s.bookSeries[0].book.toJSON()
|
||||
|
||||
let bookIndex = 0
|
||||
// if the library setting is toggled, only show later entries in series, otherwise skip
|
||||
if (library.settings.onlyShowLaterBooksInContinueSeries) {
|
||||
bookIndex = s.bookSeries.findIndex(function (b) {
|
||||
return parseFloat(b.dataValues.sequence) > s.dataValues.maxSequence
|
||||
})
|
||||
if (bookIndex === -1) {
|
||||
// no later books than maxSequence
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const libraryItem = s.bookSeries[bookIndex].book.libraryItem.toJSON()
|
||||
const book = s.bookSeries[bookIndex].book.toJSON()
|
||||
delete book.libraryItem
|
||||
libraryItem.series = {
|
||||
id: s.id,
|
||||
name: s.name,
|
||||
sequence: s.bookSeries[0].sequence
|
||||
sequence: s.bookSeries[bookIndex].sequence
|
||||
}
|
||||
if (libraryItem.feeds?.length) {
|
||||
libraryItem.rssFeed = libraryItem.feeds[0]
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@ module.exports = {
|
|||
[Sequelize.Op.gte]: 1
|
||||
})
|
||||
replacements.filterValue = value
|
||||
} else if (group === 'languages') {
|
||||
mediaWhere['language'] = value
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue