mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-07-10 19:31:39 +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
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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++
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue