Merge branch 'refs/heads/master' into mf/rssInboundManager

This commit is contained in:
mfcar 2024-05-11 20:13:40 +01:00
commit 50ea58aea2
No known key found for this signature in database
193 changed files with 11020 additions and 1005 deletions

View file

@ -15,7 +15,7 @@ const naturalSort = createNewSortInstance({
comparer: new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }).compare
})
class AuthorController {
constructor() { }
constructor() {}
async findOne(req, res) {
const include = (req.query.include || '').split(',')
@ -32,7 +32,6 @@ class AuthorController {
authorJson.libraryItems.forEach((li) => {
if (li.media.metadata.series) {
li.media.metadata.series.forEach((series) => {
const itemWithSeries = li.toJSONMinified()
itemWithSeries.media.metadata.series = series
@ -50,14 +49,14 @@ class AuthorController {
})
// Sort series items
for (const key in seriesMap) {
seriesMap[key].items = naturalSort(seriesMap[key].items).asc(li => li.media.metadata.series.sequence)
seriesMap[key].items = naturalSort(seriesMap[key].items).asc((li) => li.media.metadata.series.sequence)
}
authorJson.series = Object.values(seriesMap)
}
// Minify library items
authorJson.libraryItems = authorJson.libraryItems.map(li => li.toJSONMinified())
authorJson.libraryItems = authorJson.libraryItems.map((li) => li.toJSONMinified())
}
return res.json(authorJson)
@ -91,7 +90,8 @@ class AuthorController {
if (existingAuthor) {
const bookAuthorsToCreate = []
const itemsWithAuthor = await Database.libraryItemModel.getForAuthor(req.author)
itemsWithAuthor.forEach(libraryItem => { // Replace old author with merging author for each book
itemsWithAuthor.forEach((libraryItem) => {
// Replace old author with merging author for each book
libraryItem.media.metadata.replaceAuthor(req.author, existingAuthor)
bookAuthorsToCreate.push({
bookId: libraryItem.media.id,
@ -101,7 +101,10 @@ class AuthorController {
if (itemsWithAuthor.length) {
await Database.removeBulkBookAuthors(req.author.id) // Remove all old BookAuthor
await Database.createBulkBookAuthors(bookAuthorsToCreate) // Create all new BookAuthor
SocketAuthority.emitter('items_updated', itemsWithAuthor.map(li => li.toJSONExpanded()))
SocketAuthority.emitter(
'items_updated',
itemsWithAuthor.map((li) => li.toJSONExpanded())
)
}
// Remove old author
@ -118,7 +121,8 @@ class AuthorController {
author: existingAuthor.toJSON(),
merged: true
})
} else { // Regular author update
} else {
// Regular author update
if (req.author.update(payload)) {
hasUpdated = true
}
@ -127,12 +131,16 @@ class AuthorController {
req.author.updatedAt = Date.now()
const itemsWithAuthor = await Database.libraryItemModel.getForAuthor(req.author)
if (authorNameUpdate) { // Update author name on all books
itemsWithAuthor.forEach(libraryItem => {
if (authorNameUpdate) {
// Update author name on all books
itemsWithAuthor.forEach((libraryItem) => {
libraryItem.media.metadata.updateAuthor(req.author)
})
if (itemsWithAuthor.length) {
SocketAuthority.emitter('items_updated', itemsWithAuthor.map(li => li.toJSONExpanded()))
SocketAuthority.emitter(
'items_updated',
itemsWithAuthor.map((li) => li.toJSONExpanded())
)
}
}
@ -150,9 +158,9 @@ class AuthorController {
/**
* DELETE: /api/authors/:id
* Remove author from all books and delete
*
* @param {import('express').Request} req
* @param {import('express').Response} res
*
* @param {import('express').Request} req
* @param {import('express').Response} res
*/
async delete(req, res) {
Logger.info(`[AuthorController] Removing author "${req.author.name}"`)
@ -174,9 +182,9 @@ class AuthorController {
/**
* POST: /api/authors/:id/image
* Upload author image from web URL
*
* @param {import('express').Request} req
* @param {import('express').Response} res
*
* @param {import('express').Request} req
* @param {import('express').Response} res
*/
async uploadImage(req, res) {
if (!req.user.canUpload) {
@ -206,6 +214,7 @@ class AuthorController {
}
req.author.imagePath = result.path
req.author.updatedAt = Date.now()
await Database.authorModel.updateFromOld(req.author)
const numBooks = (await Database.libraryItemModel.getForAuthor(req.author)).length
@ -218,9 +227,9 @@ class AuthorController {
/**
* DELETE: /api/authors/:id/image
* Remove author image & delete image file
*
* @param {import('express').Request} req
* @param {import('express').Response} res
*
* @param {import('express').Request} req
* @param {import('express').Response} res
*/
async deleteImage(req, res) {
if (!req.author.imagePath) {
@ -292,10 +301,14 @@ class AuthorController {
// GET api/authors/:id/image
async getImage(req, res) {
const { query: { width, height, format, raw }, author } = req
const {
query: { width, height, format, raw },
author
} = req
if (raw) { // any value
if (!author.imagePath || !await fs.pathExists(author.imagePath)) {
if (raw) {
// any value
if (!author.imagePath || !(await fs.pathExists(author.imagePath))) {
return res.sendStatus(404)
}
@ -326,4 +339,4 @@ class AuthorController {
next()
}
}
module.exports = new AuthorController()
module.exports = new AuthorController()

View file

@ -49,8 +49,13 @@ class BackupController {
res.sendFile(req.backup.fullPath)
}
/**
*
* @param {import('express').Request} req
* @param {import('express').Response} res
*/
apply(req, res) {
this.backupManager.requestApplyBackup(req.backup, res)
this.backupManager.requestApplyBackup(this.apiCacheManager, req.backup, res)
}
middleware(req, res, next) {

View file

@ -128,7 +128,14 @@ class LibraryController {
res.json(libraryDownloadQueueDetails)
}
/**
* PATCH: /api/libraries/:id
*
* @param {import('express').Request} req
* @param {import('express').Response} res
*/
async update(req, res) {
/** @type {import('../objects/Library')} */
const library = req.library
// Validate that the custom provider exists if given any
@ -598,12 +605,12 @@ class LibraryController {
}
if (req.library.isBook) {
const authors = await authorFilters.getAuthorsWithCount(req.library.id)
const authors = await authorFilters.getAuthorsWithCount(req.library.id, 10)
const genres = await libraryItemsBookFilters.getGenresWithCount(req.library.id)
const bookStats = await libraryItemsBookFilters.getBookLibraryStats(req.library.id)
const longestBooks = await libraryItemsBookFilters.getLongestBooks(req.library.id, 10)
stats.totalAuthors = authors.length
stats.totalAuthors = await authorFilters.getAuthorsTotalCount(req.library.id)
stats.authorsWithCount = authors
stats.totalGenres = genres.length
stats.genresWithCount = genres
@ -660,6 +667,7 @@ class LibraryController {
for (const author of authors) {
const oldAuthor = author.getOldAuthor().toJSON()
oldAuthor.numBooks = author.books.length
oldAuthor.lastFirst = author.lastFirst
oldAuthors.push(oldAuthor)
}

View file

@ -117,13 +117,22 @@ class LibraryItemController {
zipHelpers.zipDirectoryPipe(libraryItemPath, filename, res)
}
//
// PATCH: will create new authors & series if in payload
//
/**
* PATCH: /items/:id/media
* Update media for a library item. Will create new authors & series when necessary
*
* @param {import('express').Request} req
* @param {import('express').Response} res
*/
async updateMedia(req, res) {
const libraryItem = req.libraryItem
const mediaPayload = req.body
if (mediaPayload.url) {
await LibraryItemController.prototype.uploadCover.bind(this)(req, res, false)
if (res.writableEnded || res.headersSent) return
}
// Book specific
if (libraryItem.isBook) {
await this.createAuthorsAndSeriesForItemUpdate(mediaPayload, libraryItem.libraryId)
@ -146,7 +155,7 @@ class LibraryItemController {
seriesRemoved = libraryItem.media.metadata.series.filter(se => !seriesIdsInUpdate.includes(se.id))
}
const hasUpdates = libraryItem.media.update(mediaPayload)
const hasUpdates = libraryItem.media.update(mediaPayload) || mediaPayload.url
if (hasUpdates) {
libraryItem.updatedAt = Date.now()
@ -171,7 +180,7 @@ class LibraryItemController {
}
// POST: api/items/:id/cover
async uploadCover(req, res) {
async uploadCover(req, res, updateAndReturnJson = true) {
if (!req.user.canUpload) {
Logger.warn('User attempted to upload a cover without permission', req.user)
return res.sendStatus(403)
@ -196,12 +205,14 @@ class LibraryItemController {
return res.status(500).send('Unknown error occurred')
}
await Database.updateLibraryItem(libraryItem)
SocketAuthority.emitter('item_updated', libraryItem.toJSONExpanded())
res.json({
success: true,
cover: result.cover
})
if (updateAndReturnJson) {
await Database.updateLibraryItem(libraryItem)
SocketAuthority.emitter('item_updated', libraryItem.toJSONExpanded())
res.json({
success: true,
cover: result.cover
})
}
}
// PATCH: api/items/:id/cover
@ -276,6 +287,9 @@ class LibraryItemController {
return res.sendStatus(404)
}
if (req.query.ts)
res.set('Cache-Control', 'private, max-age=86400')
if (raw) { // any value
if (global.XAccel) {
const encodedURI = encodeUriPath(global.XAccel + libraryItem.media.coverPath)

View file

@ -284,7 +284,7 @@ class MiscController {
}
res.json({
tags: tags
tags: tags.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()))
})
}
@ -329,6 +329,7 @@ class MiscController {
await libraryItem.media.update({
tags: libraryItem.media.tags
})
await libraryItem.saveMetadataFile()
const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem)
SocketAuthority.emitter('item_updated', oldLibraryItem.toJSONExpanded())
numItemsUpdated++
@ -370,6 +371,7 @@ class MiscController {
await libraryItem.media.update({
tags: libraryItem.media.tags
})
await libraryItem.saveMetadataFile()
const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem)
SocketAuthority.emitter('item_updated', oldLibraryItem.toJSONExpanded())
numItemsUpdated++
@ -462,6 +464,7 @@ class MiscController {
await libraryItem.media.update({
genres: libraryItem.media.genres
})
await libraryItem.saveMetadataFile()
const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem)
SocketAuthority.emitter('item_updated', oldLibraryItem.toJSONExpanded())
numItemsUpdated++
@ -503,6 +506,7 @@ class MiscController {
await libraryItem.media.update({
genres: libraryItem.media.genres
})
await libraryItem.saveMetadataFile()
const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem)
SocketAuthority.emitter('item_updated', oldLibraryItem.toJSONExpanded())
numItemsUpdated++