move num calculation inside sql

This commit is contained in:
Finn Dittmar 2026-04-05 11:58:29 +02:00
parent 64cbf59609
commit e8191b503d
No known key found for this signature in database
GPG key ID: A630219F715A1D1E
3 changed files with 70 additions and 13 deletions

View file

@ -258,7 +258,8 @@ class Book extends Model {
}
get includedAudioFiles() {
return this.audioFiles.filter((af) => !af.exclude)
const audioFiles = Array.isArray(this.audioFiles) ? this.audioFiles : []
return audioFiles.filter((af) => !af.exclude)
}
get hasMediaFiles() {
@ -328,10 +329,18 @@ class Book extends Model {
* @returns {number}
*/
get size() {
const computedSize = Number(this.dataValues?.computedMediaSize)
if (Number.isFinite(computedSize)) {
return computedSize
}
let total = 0
this.audioFiles.forEach((af) => (total += af.metadata.size))
const audioFiles = Array.isArray(this.audioFiles) ? this.audioFiles : []
audioFiles.forEach((af) => {
total += Number(af?.metadata?.size) || 0
})
if (this.ebookFile) {
total += this.ebookFile.metadata.size
total += Number(this.ebookFile?.metadata?.size) || 0
}
return total
}
@ -655,14 +664,18 @@ class Book extends Model {
throw new Error(`[Book] Cannot convert to old JSON because series are not loaded`)
}
const computedNumTracks = Number(this.dataValues?.computedNumTracks)
const computedNumAudioFiles = Number(this.dataValues?.computedNumAudioFiles)
const computedNumChapters = Number(this.dataValues?.computedNumChapters)
return {
id: this.id,
metadata: this.oldMetadataToJSONMinified(),
coverPath: this.coverPath,
tags: [...(this.tags || [])],
numTracks: this.includedAudioFiles.length,
numAudioFiles: this.audioFiles?.length || 0,
numChapters: this.chapters?.length || 0,
numTracks: Number.isFinite(computedNumTracks) ? computedNumTracks : this.includedAudioFiles.length,
numAudioFiles: Number.isFinite(computedNumAudioFiles) ? computedNumAudioFiles : this.audioFiles?.length || 0,
numChapters: Number.isFinite(computedNumChapters) ? computedNumChapters : this.chapters?.length || 0,
duration: this.duration,
size: this.size,
ebookFormat: this.ebookFile?.ebookFormat

View file

@ -491,11 +491,7 @@ class LibraryItem extends Model {
}
Logger.debug(`Loaded ${newestAuthorsPayload.authors.length} of ${newestAuthorsPayload.count} authors for "Newest Authors" in ${newestAuthorsResult.elapsedSeconds}s`)
} else if (library.isPodcast) {
const [newestEpisodesResult, mostRecentResult, mediaFinishedResult] = await Promise.all([
timed(() => libraryFilters.getNewestPodcastEpisodes(library, user, limit)),
timed(() => libraryFilters.getLibraryItemsMostRecentlyAdded(library, user, include, limit)),
timed(() => libraryFilters.getMediaFinished(library, user, include, limit))
])
const [newestEpisodesResult, mostRecentResult, mediaFinishedResult] = await Promise.all([timed(() => libraryFilters.getNewestPodcastEpisodes(library, user, limit)), timed(() => libraryFilters.getLibraryItemsMostRecentlyAdded(library, user, include, limit)), timed(() => libraryFilters.getMediaFinished(library, user, include, limit))])
const newestEpisodesPayload = newestEpisodesResult.payload
// "Newest Episodes" shelf
@ -1009,6 +1005,9 @@ class LibraryItem extends Model {
throw new Error(`[LibraryItem] Cannot convert to old JSON without media for library item "${this.id}"`)
}
const computedNumFiles = Number(this.dataValues?.computedNumFiles)
const numFiles = Number.isFinite(computedNumFiles) ? computedNumFiles : this.libraryFiles?.length || 0
return {
id: this.id,
ino: this.ino,
@ -1027,7 +1026,7 @@ class LibraryItem extends Model {
isInvalid: !!this.isInvalid,
mediaType: this.mediaType,
media: this.media.toOldJSONMinified(),
numFiles: this.libraryFiles.length,
numFiles,
size: this.size
}
}

View file

@ -158,10 +158,55 @@ module.exports = {
model: Database.bookSeriesModel,
include: {
model: Database.bookModel,
attributes: [
'id',
'title',
'subtitle',
'publishedYear',
'publishedDate',
'publisher',
'description',
'isbn',
'asin',
'language',
'explicit',
'abridged',
'coverPath',
'duration',
'narrators',
'ebookFile',
'tags',
'genres',
[
Sequelize.literal(`CASE
WHEN json_valid(audioFiles) THEN (
SELECT count(*)
FROM json_each(audioFiles) af
WHERE COALESCE(json_extract(af.value, '$.exclude'), 0) = 0
)
ELSE 0
END`),
'computedNumTracks'
],
[Sequelize.literal('CASE WHEN json_valid(audioFiles) THEN json_array_length(audioFiles) ELSE 0 END'), 'computedNumAudioFiles'],
[Sequelize.literal('CASE WHEN json_valid(chapters) THEN json_array_length(chapters) ELSE 0 END'), 'computedNumChapters'],
[
Sequelize.literal(`(
COALESCE((
SELECT SUM(COALESCE(CAST(json_extract(af.value, '$.metadata.size') AS INTEGER), 0))
FROM json_each(audioFiles) af
WHERE json_valid(audioFiles)
), 0)
+ COALESCE(CAST(json_extract(ebookFile, '$.metadata.size') AS INTEGER), 0)
)`),
'computedMediaSize'
]
],
where: userPermissionBookWhere.bookWhere,
include: [
{
model: Database.libraryItemModel
model: Database.libraryItemModel,
attributes: ['id', 'ino', 'extraData', 'libraryId', 'libraryFolderId', 'path', 'relPath', 'isFile', 'mtime', 'ctime', 'birthtime', 'createdAt', 'updatedAt', 'isMissing', 'isInvalid', 'mediaType', 'size', [Sequelize.literal('CASE WHEN json_valid(libraryFiles) THEN json_array_length(libraryFiles) ELSE 0 END'), 'computedNumFiles']]
},
{
model: Database.authorModel