Merge pull request #3761 from advplyr/remove_old_collection_object

Remove old Collection object
This commit is contained in:
advplyr 2024-12-30 17:14:07 -06:00 committed by GitHub
commit 6780ef9b37
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 123 additions and 360 deletions

View File

@ -138,7 +138,6 @@ export default {
.$post(`/api/collections/${collection.id}/batch/remove`, { books: this.selectedBookIds })
.then((updatedCollection) => {
console.log(`Books removed from collection`, updatedCollection)
this.$toast.success(this.$strings.ToastCollectionItemsRemoveSuccess)
this.processing = false
})
.catch((error) => {
@ -152,7 +151,6 @@ export default {
.$delete(`/api/collections/${collection.id}/book/${this.selectedLibraryItemId}`)
.then((updatedCollection) => {
console.log(`Book removed from collection`, updatedCollection)
this.$toast.success(this.$strings.ToastCollectionItemsRemoveSuccess)
this.processing = false
})
.catch((error) => {
@ -167,12 +165,11 @@ export default {
this.processing = true
if (this.showBatchCollectionModal) {
// BATCH Remove books
// BATCH Add books
this.$axios
.$post(`/api/collections/${collection.id}/batch/add`, { books: this.selectedBookIds })
.then((updatedCollection) => {
console.log(`Books added to collection`, updatedCollection)
this.$toast.success(this.$strings.ToastCollectionItemsAddSuccess)
this.processing = false
})
.catch((error) => {
@ -187,7 +184,6 @@ export default {
.$post(`/api/collections/${collection.id}/book`, { id: this.selectedLibraryItemId })
.then((updatedCollection) => {
console.log(`Book added to collection`, updatedCollection)
this.$toast.success(this.$strings.ToastCollectionItemsAddSuccess)
this.processing = false
})
.catch((error) => {
@ -214,7 +210,6 @@ export default {
.$post('/api/collections', newCollection)
.then((data) => {
console.log('New Collection Created', data)
this.$toast.success(`Collection "${data.name}" created`)
this.processing = false
this.newCollectionName = ''
})

View File

@ -729,7 +729,6 @@
"ToastBookmarkUpdateSuccess": "Отметката е обновена",
"ToastChaptersHaveErrors": "Главите имат грешки",
"ToastChaptersMustHaveTitles": "Главите трябва да имат заглавия",
"ToastCollectionItemsRemoveSuccess": "Елемент(и) премахнати от колекция",
"ToastCollectionRemoveSuccess": "Колекцията е премахната",
"ToastCollectionUpdateSuccess": "Колекцията е обновена",
"ToastItemCoverUpdateSuccess": "Корицата на елемента е обновена",

View File

@ -951,8 +951,6 @@
"ToastChaptersRemoved": "অধ্যায়গুলো মুছে ফেলা হয়েছে",
"ToastChaptersUpdated": "অধ্যায় আপডেট করা হয়েছে",
"ToastCollectionItemsAddFailed": "আইটেম(গুলি) সংগ্রহে যোগ করা ব্যর্থ হয়েছে",
"ToastCollectionItemsAddSuccess": "আইটেম(গুলি) সংগ্রহে যোগ করা সফল হয়েছে",
"ToastCollectionItemsRemoveSuccess": "আইটেম(গুলি) সংগ্রহ থেকে সরানো হয়েছে",
"ToastCollectionRemoveSuccess": "সংগ্রহ সরানো হয়েছে",
"ToastCollectionUpdateSuccess": "সংগ্রহ আপডেট করা হয়েছে",
"ToastCoverUpdateFailed": "কভার আপডেট ব্যর্থ হয়েছে",

View File

@ -904,8 +904,6 @@
"ToastChaptersRemoved": "Capítols eliminats",
"ToastChaptersUpdated": "Capítols actualitzats",
"ToastCollectionItemsAddFailed": "Error en afegir elements a la col·lecció",
"ToastCollectionItemsAddSuccess": "Elements afegits a la col·lecció",
"ToastCollectionItemsRemoveSuccess": "Elements eliminats de la col·lecció",
"ToastCollectionRemoveSuccess": "Col·lecció eliminada",
"ToastCollectionUpdateSuccess": "Col·lecció actualitzada",
"ToastCoverUpdateFailed": "Error en actualitzar la portada",

View File

@ -943,7 +943,6 @@
"ToastChaptersHaveErrors": "Kapitoly obsahují chyby",
"ToastChaptersMustHaveTitles": "Kapitoly musí mít názvy",
"ToastChaptersRemoved": "Kapitoly odstraněny",
"ToastCollectionItemsRemoveSuccess": "Položky odstraněny z kolekce",
"ToastCollectionRemoveSuccess": "Kolekce odstraněna",
"ToastCollectionUpdateSuccess": "Kolekce aktualizována",
"ToastCoverUpdateFailed": "Aktualizace obálky selhala",

View File

@ -640,7 +640,6 @@
"ToastBookmarkUpdateSuccess": "Bogmærke opdateret",
"ToastChaptersHaveErrors": "Kapitler har fejl",
"ToastChaptersMustHaveTitles": "Kapitler skal have titler",
"ToastCollectionItemsRemoveSuccess": "Element(er) fjernet fra samlingen",
"ToastCollectionRemoveSuccess": "Samling fjernet",
"ToastCollectionUpdateSuccess": "Samling opdateret",
"ToastItemCoverUpdateSuccess": "Varens omslag opdateret",

View File

@ -959,8 +959,6 @@
"ToastChaptersRemoved": "Kapitel entfernt",
"ToastChaptersUpdated": "Kapitel aktualisiert",
"ToastCollectionItemsAddFailed": "Das Hinzufügen von Element(en) zur Sammlung ist fehlgeschlagen",
"ToastCollectionItemsAddSuccess": "Element(e) erfolgreich zur Sammlung hinzugefügt",
"ToastCollectionItemsRemoveSuccess": "Medien aus der Sammlung entfernt",
"ToastCollectionRemoveSuccess": "Sammlung entfernt",
"ToastCollectionUpdateSuccess": "Sammlung aktualisiert",
"ToastCoverUpdateFailed": "Cover-Update fehlgeschlagen",

View File

@ -961,8 +961,6 @@
"ToastChaptersRemoved": "Chapters removed",
"ToastChaptersUpdated": "Chapters updated",
"ToastCollectionItemsAddFailed": "Item(s) added to collection failed",
"ToastCollectionItemsAddSuccess": "Item(s) added to collection success",
"ToastCollectionItemsRemoveSuccess": "Item(s) removed from collection",
"ToastCollectionRemoveSuccess": "Collection removed",
"ToastCollectionUpdateSuccess": "Collection updated",
"ToastCoverUpdateFailed": "Cover update failed",

View File

@ -959,8 +959,6 @@
"ToastChaptersRemoved": "Capítulos eliminados",
"ToastChaptersUpdated": "Capítulos actualizados",
"ToastCollectionItemsAddFailed": "Artículo(s) añadido(s) a la colección fallido(s)",
"ToastCollectionItemsAddSuccess": "Artículo(s) añadido(s) a la colección correctamente",
"ToastCollectionItemsRemoveSuccess": "Elementos(s) removidos de la colección",
"ToastCollectionRemoveSuccess": "Colección removida",
"ToastCollectionUpdateSuccess": "Colección actualizada",
"ToastCoverUpdateFailed": "Error al actualizar la cubierta",

View File

@ -713,7 +713,6 @@
"ToastBookmarkUpdateSuccess": "Järjehoidja värskendatud",
"ToastChaptersHaveErrors": "Peatükkidel on vigu",
"ToastChaptersMustHaveTitles": "Peatükkidel peab olema pealkiri",
"ToastCollectionItemsRemoveSuccess": "Üksus(ed) eemaldatud kogumist",
"ToastCollectionRemoveSuccess": "Kogum eemaldatud",
"ToastCollectionUpdateSuccess": "Kogum värskendatud",
"ToastItemCoverUpdateSuccess": "Üksuse kaas värskendatud",

View File

@ -953,8 +953,6 @@
"ToastChaptersRemoved": "Chapitres supprimés",
"ToastChaptersUpdated": "Chapitres mis à jour",
"ToastCollectionItemsAddFailed": "Échec de lajout de(s) élément(s) à la collection",
"ToastCollectionItemsAddSuccess": "Ajout de(s) élément(s) à la collection réussi",
"ToastCollectionItemsRemoveSuccess": "Élément(s) supprimé(s) de la collection",
"ToastCollectionRemoveSuccess": "Collection supprimée",
"ToastCollectionUpdateSuccess": "Collection mise à jour",
"ToastCoverUpdateFailed": "Échec de la mise à jour de la couverture",

View File

@ -744,7 +744,6 @@
"ToastBookmarkUpdateSuccess": "הסימניה עודכנה בהצלחה",
"ToastChaptersHaveErrors": "פרקים מכילים שגיאות",
"ToastChaptersMustHaveTitles": "פרקים חייבים לכלול כותרות",
"ToastCollectionItemsRemoveSuccess": "הפריט(ים) הוסרו מהאוסף בהצלחה",
"ToastCollectionRemoveSuccess": "האוסף הוסר בהצלחה",
"ToastCollectionUpdateSuccess": "האוסף עודכן בהצלחה",
"ToastItemCoverUpdateSuccess": "כריכת הפריט עודכנה בהצלחה",

View File

@ -959,8 +959,6 @@
"ToastChaptersRemoved": "Poglavlja uklonjena",
"ToastChaptersUpdated": "Poglavlja su ažurirana",
"ToastCollectionItemsAddFailed": "Neuspješno dodavanje stavki u zbirku",
"ToastCollectionItemsAddSuccess": "Uspješno dodavanje stavki u zbirku",
"ToastCollectionItemsRemoveSuccess": "Stavke izbrisane iz zbirke",
"ToastCollectionRemoveSuccess": "Zbirka izbrisana",
"ToastCollectionUpdateSuccess": "Zbirka ažurirana",
"ToastCoverUpdateFailed": "Ažuriranje naslovnice nije uspjelo",

View File

@ -945,7 +945,6 @@
"ToastChaptersMustHaveTitles": "A fejezeteknek címekkel kell rendelkezniük",
"ToastChaptersRemoved": "Fejezetek eltávolítva",
"ToastChaptersUpdated": "Fejezetek frissítve",
"ToastCollectionItemsRemoveSuccess": "Elem(ek) eltávolítva a gyűjteményből",
"ToastCollectionRemoveSuccess": "Gyűjtemény eltávolítva",
"ToastCollectionUpdateSuccess": "Gyűjtemény frissítve",
"ToastCoverUpdateFailed": "A borító frissítése nem sikerült",

View File

@ -950,8 +950,6 @@
"ToastChaptersRemoved": "Capitoli rimossi",
"ToastChaptersUpdated": "Capitoli aggiornati",
"ToastCollectionItemsAddFailed": "l'aggiunta dell'elemento(i) alla raccolta non è riuscito",
"ToastCollectionItemsAddSuccess": "L'aggiunta dell'elemento(i) alla raccolta è riuscito",
"ToastCollectionItemsRemoveSuccess": "Oggetto(i) rimossi dalla Raccolta",
"ToastCollectionRemoveSuccess": "Collezione rimossa",
"ToastCollectionUpdateSuccess": "Raccolta aggiornata",
"ToastCoverUpdateFailed": "Aggiornamento cover fallito",

View File

@ -666,8 +666,6 @@
"ToastChaptersMustHaveTitles": "Skyriai turi turėti pavadinimus",
"ToastChaptersRemoved": "Skyriai pašalinti",
"ToastCollectionItemsAddFailed": "Nepavyko pridėti į kolekciją",
"ToastCollectionItemsAddSuccess": "Pridėta į kolekciją",
"ToastCollectionItemsRemoveSuccess": "Elementai pašalinti iš kolekcijos",
"ToastCollectionRemoveSuccess": "Kolekcija pašalinta",
"ToastCollectionUpdateSuccess": "Kolekcija atnaujinta",
"ToastCoverUpdateFailed": "Viršelio atnaujinimas nepavyko",

View File

@ -946,8 +946,6 @@
"ToastChaptersRemoved": "Hoofdstukken verwijderd",
"ToastChaptersUpdated": "Hoofdstukken bijgewerkt",
"ToastCollectionItemsAddFailed": "Item(s) toegevoegd aan collectie mislukt",
"ToastCollectionItemsAddSuccess": "Item(s) toegevoegd aan collectie gelukt",
"ToastCollectionItemsRemoveSuccess": "Onderdeel (of onderdelen) verwijderd uit collectie",
"ToastCollectionRemoveSuccess": "Collectie verwijderd",
"ToastCollectionUpdateSuccess": "Collectie bijgewerkt",
"ToastCoverUpdateFailed": "Cover update mislukt",

View File

@ -882,8 +882,6 @@
"ToastChaptersRemoved": "Kapitler fjernet",
"ToastChaptersUpdated": "Kapitler oppdatert",
"ToastCollectionItemsAddFailed": "Feil med å legge til element(er)",
"ToastCollectionItemsAddSuccess": "Element(er) lagt til samlingen",
"ToastCollectionItemsRemoveSuccess": "Gjenstand(er) fjernet fra samling",
"ToastCollectionRemoveSuccess": "Samling fjernet",
"ToastCollectionUpdateSuccess": "samlingupdated",
"ToastCoverUpdateFailed": "Oppdatering av bilde feilet",

View File

@ -772,7 +772,6 @@
"ToastBookmarkCreateSuccess": "Dodano zakładkę",
"ToastBookmarkRemoveSuccess": "Zakładka została usunięta",
"ToastBookmarkUpdateSuccess": "Zaktualizowano zakładkę",
"ToastCollectionItemsRemoveSuccess": "Przedmiot(y) zostały usunięte z kolekcji",
"ToastCollectionRemoveSuccess": "Kolekcja usunięta",
"ToastCollectionUpdateSuccess": "Zaktualizowano kolekcję",
"ToastItemCoverUpdateSuccess": "Zaktualizowano okładkę",

View File

@ -735,7 +735,6 @@
"ToastCachePurgeSuccess": "Cache apagado com sucesso",
"ToastChaptersHaveErrors": "Capítulos com erro",
"ToastChaptersMustHaveTitles": "Capítulos precisam ter títulos",
"ToastCollectionItemsRemoveSuccess": "Item(ns) removidos da coleção",
"ToastCollectionRemoveSuccess": "Coleção removida",
"ToastCollectionUpdateSuccess": "Coleção atualizada",
"ToastDeleteFileFailed": "Falha ao apagar arquivo",

View File

@ -959,8 +959,6 @@
"ToastChaptersRemoved": "Удалены главы",
"ToastChaptersUpdated": "Обновленные главы",
"ToastCollectionItemsAddFailed": "Не удалось добавить элемент(ы) в коллекцию",
"ToastCollectionItemsAddSuccess": "Элемент(ы) добавлены в коллекцию",
"ToastCollectionItemsRemoveSuccess": "Элемент(ы), удалены из коллекции",
"ToastCollectionRemoveSuccess": "Коллекция удалена",
"ToastCollectionUpdateSuccess": "Коллекция обновлена",
"ToastCoverUpdateFailed": "Не удалось обновить обложку",

View File

@ -959,8 +959,6 @@
"ToastChaptersRemoved": "Poglavja so odstranjena",
"ToastChaptersUpdated": "Poglavja so posodobljena",
"ToastCollectionItemsAddFailed": "Dodajanje elementov v zbirko ni uspelo",
"ToastCollectionItemsAddSuccess": "Dodajanje elementov v zbirko je bilo uspešno",
"ToastCollectionItemsRemoveSuccess": "Elementi so bili odstranjeni iz zbirke",
"ToastCollectionRemoveSuccess": "Zbirka je bila odstranjena",
"ToastCollectionUpdateSuccess": "Zbirka je bila posodobljena",
"ToastCoverUpdateFailed": "Posodobitev naslovnice ni uspela",

View File

@ -705,7 +705,6 @@
"ToastBookmarkUpdateSuccess": "Bokmärket har uppdaterats",
"ToastChaptersHaveErrors": "Kapitlen har fel",
"ToastChaptersMustHaveTitles": "Kapitel måste ha titlar",
"ToastCollectionItemsRemoveSuccess": "Objekt borttagna från samlingen",
"ToastCollectionRemoveSuccess": "Samlingen har raderats",
"ToastCollectionUpdateSuccess": "Samlingen har uppdaterats",
"ToastItemCoverUpdateSuccess": "Objektets omslag uppdaterat",

View File

@ -959,8 +959,6 @@
"ToastChaptersRemoved": "Розділи видалені",
"ToastChaptersUpdated": "Розділи оновлені",
"ToastCollectionItemsAddFailed": "Не вдалося додати елемент(и) до колекції",
"ToastCollectionItemsAddSuccess": "Елемент(и) успішно додано до колекції",
"ToastCollectionItemsRemoveSuccess": "Елемент(и) видалено з добірки",
"ToastCollectionRemoveSuccess": "Добірку видалено",
"ToastCollectionUpdateSuccess": "Добірку оновлено",
"ToastCoverUpdateFailed": "Не вдалося оновити обкладинку",

View File

@ -683,7 +683,6 @@
"ToastBookmarkUpdateSuccess": "Đánh dấu đã được cập nhật",
"ToastChaptersHaveErrors": "Các chương có lỗi",
"ToastChaptersMustHaveTitles": "Các chương phải có tiêu đề",
"ToastCollectionItemsRemoveSuccess": "Mục đã được xóa khỏi bộ sưu tập",
"ToastCollectionRemoveSuccess": "Bộ sưu tập đã được xóa",
"ToastCollectionUpdateSuccess": "Bộ sưu tập đã được cập nhật",
"ToastItemCoverUpdateSuccess": "Ảnh bìa mục đã được cập nhật",

View File

@ -959,8 +959,6 @@
"ToastChaptersRemoved": "已删除章节",
"ToastChaptersUpdated": "章节已更新",
"ToastCollectionItemsAddFailed": "项目添加到收藏夹失败",
"ToastCollectionItemsAddSuccess": "项目添加到收藏夹成功",
"ToastCollectionItemsRemoveSuccess": "项目从收藏夹移除",
"ToastCollectionRemoveSuccess": "收藏夹已删除",
"ToastCollectionUpdateSuccess": "收藏夹已更新",
"ToastCoverUpdateFailed": "封面更新失败",

View File

@ -727,7 +727,6 @@
"ToastBookmarkUpdateSuccess": "書籤已更新",
"ToastChaptersHaveErrors": "章節有錯誤",
"ToastChaptersMustHaveTitles": "章節必須有標題",
"ToastCollectionItemsRemoveSuccess": "項目從收藏夾移除",
"ToastCollectionRemoveSuccess": "收藏夾已刪除",
"ToastCollectionUpdateSuccess": "收藏夾已更新",
"ToastItemCoverUpdateSuccess": "項目封面已更新",

View File

@ -406,11 +406,6 @@ class Database {
return Promise.all(oldBooks.map((oldBook) => this.models.book.saveFromOld(oldBook)))
}
createBulkCollectionBooks(collectionBooks) {
if (!this.sequelize) return false
return this.models.collectionBook.bulkCreate(collectionBooks)
}
createPlaylistMediaItem(playlistMediaItem) {
if (!this.sequelize) return false
return this.models.playlistMediaItem.create(playlistMediaItem)

View File

@ -5,13 +5,17 @@ const SocketAuthority = require('../SocketAuthority')
const Database = require('../Database')
const RssFeedManager = require('../managers/RssFeedManager')
const Collection = require('../objects/Collection')
/**
* @typedef RequestUserObject
* @property {import('../models/User')} user
*
* @typedef {Request & RequestUserObject} RequestWithUser
*
* @typedef RequestEntityObject
* @property {import('../models/Collection')} collection
*
* @typedef {RequestWithUser & RequestEntityObject} CollectionControllerRequest
*/
class CollectionController {
@ -25,36 +29,68 @@ class CollectionController {
* @param {Response} res
*/
async create(req, res) {
const newCollection = new Collection()
req.body.userId = req.user.id
if (!newCollection.setData(req.body)) {
const reqBody = req.body || {}
// Validation
if (!reqBody.name || !reqBody.libraryId) {
return res.status(400).send('Invalid collection data')
}
const libraryItemIds = (reqBody.books || []).filter((b) => !!b && typeof b == 'string')
if (!libraryItemIds.length) {
return res.status(400).send('Invalid collection data. No books')
}
// Create collection record
await Database.collectionModel.createFromOld(newCollection)
// Get library items in collection
const libraryItemsInCollection = await Database.libraryItemModel.getForCollection(newCollection)
// Create collectionBook records
let order = 1
const collectionBooksToAdd = []
for (const libraryItemId of newCollection.books) {
const libraryItem = libraryItemsInCollection.find((li) => li.id === libraryItemId)
if (libraryItem) {
collectionBooksToAdd.push({
collectionId: newCollection.id,
bookId: libraryItem.media.id,
order: order++
})
// Load library items
const libraryItems = await Database.libraryItemModel.findAll({
attributes: ['id', 'mediaId', 'mediaType', 'libraryId'],
where: {
id: libraryItemIds,
libraryId: reqBody.libraryId,
mediaType: 'book'
}
}
if (collectionBooksToAdd.length) {
await Database.createBulkCollectionBooks(collectionBooksToAdd)
})
if (libraryItems.length !== libraryItemIds.length) {
return res.status(400).send('Invalid collection data. Invalid books')
}
const jsonExpanded = newCollection.toJSONExpanded(libraryItemsInCollection)
/** @type {import('../models/Collection')} */
let newCollection = null
const transaction = await Database.sequelize.transaction()
try {
// Create collection
newCollection = await Database.collectionModel.create(
{
libraryId: reqBody.libraryId,
name: reqBody.name,
description: reqBody.description || null
},
{ transaction }
)
// Create collectionBooks
const collectionBookPayloads = libraryItemIds.map((llid, index) => {
const libraryItem = libraryItems.find((li) => li.id === llid)
return {
collectionId: newCollection.id,
bookId: libraryItem.mediaId,
order: index + 1
}
})
await Database.collectionBookModel.bulkCreate(collectionBookPayloads, { transaction })
await transaction.commit()
} catch (error) {
await transaction.rollback()
Logger.error('[CollectionController] create:', error)
return res.status(500).send('Failed to create collection')
}
// Load books expanded
newCollection.books = await newCollection.getBooksExpandedWithLibraryItem()
// Note: The old collection model stores expanded libraryItems in the books property
const jsonExpanded = newCollection.toOldJSONExpanded()
SocketAuthority.emitter('collection_added', jsonExpanded)
res.json(jsonExpanded)
}
@ -75,7 +111,7 @@ class CollectionController {
/**
* GET: /api/collections/:id
*
* @param {RequestWithUser} req
* @param {CollectionControllerRequest} req
* @param {Response} res
*/
async findOne(req, res) {
@ -94,7 +130,7 @@ class CollectionController {
* PATCH: /api/collections/:id
* Update collection
*
* @param {RequestWithUser} req
* @param {CollectionControllerRequest} req
* @param {Response} res
*/
async update(req, res) {
@ -158,7 +194,7 @@ class CollectionController {
*
* @this {import('../routers/ApiRouter')}
*
* @param {RequestWithUser} req
* @param {CollectionControllerRequest} req
* @param {Response} res
*/
async delete(req, res) {
@ -178,7 +214,7 @@ class CollectionController {
* Add a single book to a collection
* Req.body { id: <library item id> }
*
* @param {RequestWithUser} req
* @param {CollectionControllerRequest} req
* @param {Response} res
*/
async addBook(req, res) {
@ -212,7 +248,7 @@ class CollectionController {
* Remove a single book from a collection. Re-order books
* TODO: bookId is actually libraryItemId. Clients need updating to use bookId
*
* @param {RequestWithUser} req
* @param {CollectionControllerRequest} req
* @param {Response} res
*/
async removeBook(req, res) {
@ -257,29 +293,31 @@ class CollectionController {
* Add multiple books to collection
* Req.body { books: <Array of library item ids> }
*
* @param {RequestWithUser} req
* @param {CollectionControllerRequest} req
* @param {Response} res
*/
async addBatch(req, res) {
// filter out invalid libraryItemIds
const bookIdsToAdd = (req.body.books || []).filter((b) => !!b && typeof b == 'string')
if (!bookIdsToAdd.length) {
return res.status(500).send('Invalid request body')
return res.status(400).send('Invalid request body')
}
// Get library items associated with ids
const libraryItems = await Database.libraryItemModel.findAll({
attributes: ['id', 'mediaId', 'mediaType', 'libraryId'],
where: {
id: {
[Sequelize.Op.in]: bookIdsToAdd
}
},
include: {
model: Database.bookModel
id: bookIdsToAdd,
libraryId: req.collection.libraryId,
mediaType: 'book'
}
})
if (!libraryItems.length) {
return res.status(400).send('Invalid request body. No valid books')
}
// Get collection books already in collection
/** @type {import('../models/CollectionBook')[]} */
const collectionBooks = await req.collection.getCollectionBooks()
let order = collectionBooks.length + 1
@ -288,10 +326,10 @@ class CollectionController {
// Check and set new collection books to add
for (const libraryItem of libraryItems) {
if (!collectionBooks.some((cb) => cb.bookId === libraryItem.media.id)) {
if (!collectionBooks.some((cb) => cb.bookId === libraryItem.mediaId)) {
collectionBooksToAdd.push({
collectionId: req.collection.id,
bookId: libraryItem.media.id,
bookId: libraryItem.mediaId,
order: order++
})
hasUpdated = true
@ -302,7 +340,8 @@ class CollectionController {
let jsonExpanded = null
if (hasUpdated) {
await Database.createBulkCollectionBooks(collectionBooksToAdd)
await Database.collectionBookModel.bulkCreate(collectionBooksToAdd)
jsonExpanded = await req.collection.getOldJsonExpanded()
SocketAuthority.emitter('collection_updated', jsonExpanded)
} else {
@ -316,7 +355,7 @@ class CollectionController {
* Remove multiple books from collection
* Req.body { books: <Array of library item ids> }
*
* @param {RequestWithUser} req
* @param {CollectionControllerRequest} req
* @param {Response} res
*/
async removeBatch(req, res) {
@ -329,9 +368,7 @@ class CollectionController {
// Get library items associated with ids
const libraryItems = await Database.libraryItemModel.findAll({
where: {
id: {
[Sequelize.Op.in]: bookIdsToRemove
}
id: bookIdsToRemove
},
include: {
model: Database.bookModel
@ -339,6 +376,7 @@ class CollectionController {
})
// Get collection books already in collection
/** @type {import('../models/CollectionBook')[]} */
const collectionBooks = await req.collection.getCollectionBooks({
order: [['order', 'ASC']]
})

View File

@ -1,7 +1,5 @@
const { DataTypes, Model, Sequelize } = require('sequelize')
const oldCollection = require('../objects/Collection')
class Collection extends Model {
constructor(values, options) {
super(values, options)
@ -26,12 +24,12 @@ class Collection extends Model {
}
/**
* Get all old collections toJSONExpanded, items filtered for user permissions
* Get all toOldJSONExpanded, items filtered for user permissions
*
* @param {import('./User')} user
* @param {string} [libraryId]
* @param {string[]} [include]
* @returns {Promise<oldCollection[]>} oldCollection.toJSONExpanded
* @async
*/
static async getOldCollectionsJsonExpanded(user, libraryId, include) {
let collectionWhere = null
@ -79,8 +77,6 @@ class Collection extends Model {
// TODO: Handle user permission restrictions on initial query
return collections
.map((c) => {
const oldCollection = this.getOldCollection(c)
// Filter books using user permissions
const books =
c.books?.filter((b) => {
@ -95,20 +91,14 @@ class Collection extends Model {
return true
}) || []
// Map to library items
const libraryItems = books.map((b) => {
const libraryItem = b.libraryItem
delete b.libraryItem
libraryItem.media = b
return this.sequelize.models.libraryItem.getOldLibraryItem(libraryItem)
})
// Users with restricted permissions will not see this collection
if (!books.length && oldCollection.books.length) {
if (!books.length && c.books.length) {
return null
}
const collectionExpanded = oldCollection.toJSONExpanded(libraryItems)
this.books = books
const collectionExpanded = c.toOldJSONExpanded()
// Map feed if found
if (c.feeds?.length) {
@ -153,69 +143,6 @@ class Collection extends Model {
})
}
/**
* Get old collection from Collection
* @param {Collection} collectionExpanded
* @returns {oldCollection}
*/
static getOldCollection(collectionExpanded) {
const libraryItemIds = collectionExpanded.books?.map((b) => b.libraryItem?.id || null).filter((lid) => lid) || []
return new oldCollection({
id: collectionExpanded.id,
libraryId: collectionExpanded.libraryId,
name: collectionExpanded.name,
description: collectionExpanded.description,
books: libraryItemIds,
lastUpdate: collectionExpanded.updatedAt.valueOf(),
createdAt: collectionExpanded.createdAt.valueOf()
})
}
/**
*
* @param {oldCollection} oldCollection
* @returns {Promise<Collection>}
*/
static createFromOld(oldCollection) {
const collection = this.getFromOld(oldCollection)
return this.create(collection)
}
static getFromOld(oldCollection) {
return {
id: oldCollection.id,
name: oldCollection.name,
description: oldCollection.description,
libraryId: oldCollection.libraryId
}
}
static removeById(collectionId) {
return this.destroy({
where: {
id: collectionId
}
})
}
/**
* Get old collection by id
* @param {string} collectionId
* @returns {Promise<oldCollection|null>} returns null if not found
*/
static async getOldById(collectionId) {
if (!collectionId) return null
const collection = await this.findByPk(collectionId, {
include: {
model: this.sequelize.models.book,
include: this.sequelize.models.libraryItem
},
order: [[this.sequelize.models.book, this.sequelize.models.collectionBook, 'order', 'ASC']]
})
if (!collection) return null
return this.getOldCollection(collection)
}
/**
* Remove all collections belonging to library
* @param {string} libraryId
@ -286,64 +213,37 @@ class Collection extends Model {
}
/**
* Get old collection toJSONExpanded, items filtered for user permissions
* Get toOldJSONExpanded, items filtered for user permissions
*
* @param {import('./User')|null} user
* @param {string[]} [include]
* @returns {Promise<oldCollection>} oldCollection.toJSONExpanded
* @async
*/
async getOldJsonExpanded(user, include) {
this.books =
(await this.getBooks({
include: [
{
model: this.sequelize.models.libraryItem
},
{
model: this.sequelize.models.author,
through: {
attributes: []
}
},
{
model: this.sequelize.models.series,
through: {
attributes: ['sequence']
}
}
],
order: [Sequelize.literal('`collectionBook.order` ASC')]
})) || []
this.books = await this.getBooksExpandedWithLibraryItem()
// Filter books using user permissions
// TODO: Handle user permission restrictions on initial query
const books =
this.books?.filter((b) => {
if (user) {
if (b.tags?.length && !user.checkCanAccessLibraryItemWithTags(b.tags)) {
return false
}
if (b.explicit === true && !user.canAccessExplicitContent) {
return false
}
if (user) {
const books = this.books.filter((b) => {
if (b.tags?.length && !user.checkCanAccessLibraryItemWithTags(b.tags)) {
return false
}
if (b.explicit === true && !user.canAccessExplicitContent) {
return false
}
return true
}) || []
})
// Map to library items
const libraryItems = books.map((b) => {
const libraryItem = b.libraryItem
delete b.libraryItem
libraryItem.media = b
return this.sequelize.models.libraryItem.getOldLibraryItem(libraryItem)
})
// Users with restricted permissions will not see this collection
if (!books.length && this.books.length) {
return null
}
// Users with restricted permissions will not see this collection
if (!books.length && this.books.length) {
return null
this.books = books
}
const collectionExpanded = this.toOldJSONExpanded(libraryItems)
const collectionExpanded = this.toOldJSONExpanded()
if (include?.includes('rssfeed')) {
const feeds = await this.getFeeds()
@ -357,10 +257,10 @@ class Collection extends Model {
/**
*
* @param {string[]} libraryItemIds
* @param {string[]} [libraryItemIds=[]]
* @returns
*/
toOldJSON(libraryItemIds) {
toOldJSON(libraryItemIds = []) {
return {
id: this.id,
libraryId: this.libraryId,
@ -372,19 +272,19 @@ class Collection extends Model {
}
}
/**
*
* @param {import('../objects/LibraryItem')} oldLibraryItems
* @returns
*/
toOldJSONExpanded(oldLibraryItems) {
const json = this.toOldJSON(oldLibraryItems.map((li) => li.id))
json.books = json.books
.map((libraryItemId) => {
const book = oldLibraryItems.find((li) => li.id === libraryItemId)
return book ? book.toJSONExpanded() : null
})
.filter((b) => !!b)
toOldJSONExpanded() {
if (!this.books) {
throw new Error('Books are required to expand Collection')
}
const json = this.toOldJSON()
json.books = this.books.map((book) => {
const libraryItem = book.libraryItem
delete book.libraryItem
libraryItem.media = book
return this.sequelize.models.libraryItem.getOldLibraryItem(libraryItem).toJSONExpanded()
})
return json
}
}

View File

@ -16,15 +16,6 @@ class CollectionBook extends Model {
this.createdAt
}
static removeByIds(collectionId, bookId) {
return this.destroy({
where: {
bookId,
collectionId
}
})
}
static init(sequelize) {
super.init(
{

View File

@ -123,7 +123,7 @@ class LibraryItem extends Model {
}
/**
* Currently unused because this is too slow and uses too much mem
*
* @param {import('sequelize').WhereOptions} [where]
* @returns {Array<objects.LibraryItem>} old library items
*/

View File

@ -1,115 +0,0 @@
const uuidv4 = require("uuid").v4
class Collection {
constructor(collection) {
this.id = null
this.libraryId = null
this.name = null
this.description = null
this.cover = null
this.coverFullPath = null
this.books = []
this.lastUpdate = null
this.createdAt = null
if (collection) {
this.construct(collection)
}
}
toJSON() {
return {
id: this.id,
libraryId: this.libraryId,
name: this.name,
description: this.description,
cover: this.cover,
coverFullPath: this.coverFullPath,
books: [...this.books],
lastUpdate: this.lastUpdate,
createdAt: this.createdAt
}
}
toJSONExpanded(libraryItems, minifiedBooks = false) {
const json = this.toJSON()
json.books = json.books.map(bookId => {
const book = libraryItems.find(li => li.id === bookId)
return book ? minifiedBooks ? book.toJSONMinified() : book.toJSONExpanded() : null
}).filter(b => !!b)
return json
}
// Expanded and filtered out items not accessible to user
toJSONExpandedForUser(user, libraryItems) {
const json = this.toJSON()
json.books = json.books.map(libraryItemId => {
const libraryItem = libraryItems.find(li => li.id === libraryItemId)
return libraryItem ? libraryItem.toJSONExpanded() : null
}).filter(li => {
return li && user.checkCanAccessLibraryItem(li)
})
return json
}
construct(collection) {
this.id = collection.id
this.libraryId = collection.libraryId
this.name = collection.name
this.description = collection.description || null
this.cover = collection.cover || null
this.coverFullPath = collection.coverFullPath || null
this.books = collection.books ? [...collection.books] : []
this.lastUpdate = collection.lastUpdate || null
this.createdAt = collection.createdAt || null
}
setData(data) {
if (!data.libraryId || !data.name) {
return false
}
this.id = uuidv4()
this.libraryId = data.libraryId
this.name = data.name
this.description = data.description || null
this.cover = data.cover || null
this.coverFullPath = data.coverFullPath || null
this.books = data.books ? [...data.books] : []
this.lastUpdate = Date.now()
this.createdAt = Date.now()
return true
}
addBook(bookId) {
this.books.push(bookId)
this.lastUpdate = Date.now()
}
removeBook(bookId) {
this.books = this.books.filter(bid => bid !== bookId)
this.lastUpdate = Date.now()
}
update(payload) {
let hasUpdates = false
for (const key in payload) {
if (key === 'books') {
if (payload.books && this.books.join(',') !== payload.books.join(',')) {
this.books = [...payload.books]
hasUpdates = true
}
} else if (this[key] !== undefined && this[key] !== payload[key]) {
hasUpdates = true
this[key] = payload[key]
}
}
if (hasUpdates) {
this.lastUpdate = Date.now()
}
return hasUpdates
}
}
module.exports = Collection