diff --git a/server/models/Author.js b/server/models/Author.js index c6537ec1..cb695386 100644 --- a/server/models/Author.js +++ b/server/models/Author.js @@ -26,11 +26,6 @@ class Author extends Model { this.createdAt } - static async getOldAuthors() { - const authors = await this.findAll() - return authors.map(au => au.getOldAuthor()) - } - getOldAuthor() { return new oldAuthor({ id: this.id, @@ -85,7 +80,7 @@ class Author extends Model { /** * Get oldAuthor by id - * @param {string} authorId + * @param {string} authorId * @returns {Promise} */ static async getOldById(authorId) { @@ -96,7 +91,7 @@ class Author extends Model { /** * Check if author exists - * @param {string} authorId + * @param {string} authorId * @returns {Promise} */ static async checkExistsById(authorId) { @@ -106,60 +101,67 @@ class Author extends Model { /** * Get old author by name and libraryId. name case insensitive * TODO: Look for authors ignoring punctuation - * - * @param {string} authorName - * @param {string} libraryId + * + * @param {string} authorName + * @param {string} libraryId * @returns {Promise} */ static async getOldByNameAndLibrary(authorName, libraryId) { - const author = (await this.findOne({ - where: [ - where(fn('lower', col('name')), authorName.toLowerCase()), - { - libraryId - } - ] - }))?.getOldAuthor() + const author = ( + await this.findOne({ + where: [ + where(fn('lower', col('name')), authorName.toLowerCase()), + { + libraryId + } + ] + }) + )?.getOldAuthor() return author } /** * Initialize model - * @param {import('../Database').sequelize} sequelize + * @param {import('../Database').sequelize} sequelize */ static init(sequelize) { - super.init({ - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true - }, - name: DataTypes.STRING, - lastFirst: DataTypes.STRING, - asin: DataTypes.STRING, - description: DataTypes.TEXT, - imagePath: DataTypes.STRING - }, { - sequelize, - modelName: 'author', - indexes: [ - { - fields: [{ - name: 'name', - collate: 'NOCASE' - }] + super.init( + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true }, - // { - // fields: [{ - // name: 'lastFirst', - // collate: 'NOCASE' - // }] - // }, - { - fields: ['libraryId'] - } - ] - }) + name: DataTypes.STRING, + lastFirst: DataTypes.STRING, + asin: DataTypes.STRING, + description: DataTypes.TEXT, + imagePath: DataTypes.STRING + }, + { + sequelize, + modelName: 'author', + indexes: [ + { + fields: [ + { + name: 'name', + collate: 'NOCASE' + } + ] + }, + // { + // fields: [{ + // name: 'lastFirst', + // collate: 'NOCASE' + // }] + // }, + { + fields: ['libraryId'] + } + ] + } + ) const { library } = sequelize.models library.hasMany(Author, { diff --git a/server/models/Book.js b/server/models/Book.js index e2b56fbe..a8ccf73d 100644 --- a/server/models/Book.js +++ b/server/models/Book.js @@ -21,13 +21,13 @@ const Logger = require('../Logger') /** * @typedef SeriesExpandedProperties * @property {{sequence:string}} bookSeries - * + * * @typedef {import('./Series') & SeriesExpandedProperties} SeriesExpanded - * + * * @typedef BookExpandedProperties * @property {import('./Author')[]} authors * @property {SeriesExpanded[]} series - * + * * @typedef {Book & BookExpandedProperties} BookExpanded */ @@ -112,29 +112,31 @@ class Book extends Model { const bookExpanded = libraryItemExpanded.media let authors = [] if (bookExpanded.authors?.length) { - authors = bookExpanded.authors.map(au => { + authors = bookExpanded.authors.map((au) => { return { id: au.id, name: au.name } }) } else if (bookExpanded.bookAuthors?.length) { - authors = bookExpanded.bookAuthors.map(ba => { - if (ba.author) { - return { - id: ba.author.id, - name: ba.author.name + authors = bookExpanded.bookAuthors + .map((ba) => { + if (ba.author) { + return { + id: ba.author.id, + name: ba.author.name + } + } else { + Logger.error(`[Book] Invalid bookExpanded bookAuthors: no author`, ba) + return null } - } else { - Logger.error(`[Book] Invalid bookExpanded bookAuthors: no author`, ba) - return null - } - }).filter(a => a) + }) + .filter((a) => a) } let series = [] if (bookExpanded.series?.length) { - series = bookExpanded.series.map(se => { + series = bookExpanded.series.map((se) => { return { id: se.id, name: se.name, @@ -142,18 +144,20 @@ class Book extends Model { } }) } else if (bookExpanded.bookSeries?.length) { - series = bookExpanded.bookSeries.map(bs => { - if (bs.series) { - return { - id: bs.series.id, - name: bs.series.name, - sequence: bs.sequence + series = bookExpanded.bookSeries + .map((bs) => { + if (bs.series) { + return { + id: bs.series.id, + name: bs.series.name, + sequence: bs.sequence + } + } else { + Logger.error(`[Book] Invalid bookExpanded bookSeries: no series`, bs) + return null } - } else { - Logger.error(`[Book] Invalid bookExpanded bookSeries: no series`, bs) - return null - } - }).filter(s => s) + }) + .filter((s) => s) } return { @@ -185,7 +189,7 @@ class Book extends Model { } /** - * @param {object} oldBook + * @param {object} oldBook * @returns {boolean} true if updated */ static saveFromOld(oldBook) { @@ -194,10 +198,12 @@ class Book extends Model { where: { id: book.id } - }).then(result => result[0] > 0).catch((error) => { - Logger.error(`[Book] Failed to save book ${book.id}`, error) - return false }) + .then((result) => result[0] > 0) + .catch((error) => { + Logger.error(`[Book] Failed to save book ${book.id}`, error) + return false + }) } static getFromOld(oldBook) { @@ -219,7 +225,7 @@ class Book extends Model { ebookFile: oldBook.ebookFile?.toJSON() || null, coverPath: oldBook.coverPath, duration: oldBook.duration, - audioFiles: oldBook.audioFiles?.map(af => af.toJSON()) || [], + audioFiles: oldBook.audioFiles?.map((af) => af.toJSON()) || [], chapters: oldBook.chapters, tags: oldBook.tags, genres: oldBook.metadata.genres @@ -229,12 +235,12 @@ class Book extends Model { getAbsMetadataJson() { return { tags: this.tags || [], - chapters: this.chapters?.map(c => ({ ...c })) || [], + chapters: this.chapters?.map((c) => ({ ...c })) || [], title: this.title, subtitle: this.subtitle, - authors: this.authors.map(a => a.name), + authors: this.authors.map((a) => a.name), narrators: this.narrators, - series: this.series.map(se => { + series: this.series.map((se) => { const sequence = se.bookSeries?.sequence || '' if (!sequence) return se.name return `${se.name} #${sequence}` @@ -254,61 +260,66 @@ class Book extends Model { /** * Initialize model - * @param {import('../Database').sequelize} sequelize + * @param {import('../Database').sequelize} sequelize */ static init(sequelize) { - super.init({ - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true - }, - title: DataTypes.STRING, - titleIgnorePrefix: DataTypes.STRING, - subtitle: DataTypes.STRING, - publishedYear: DataTypes.STRING, - publishedDate: DataTypes.STRING, - publisher: DataTypes.STRING, - description: DataTypes.TEXT, - isbn: DataTypes.STRING, - asin: DataTypes.STRING, - language: DataTypes.STRING, - explicit: DataTypes.BOOLEAN, - abridged: DataTypes.BOOLEAN, - coverPath: DataTypes.STRING, - duration: DataTypes.FLOAT, + super.init( + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + title: DataTypes.STRING, + titleIgnorePrefix: DataTypes.STRING, + subtitle: DataTypes.STRING, + publishedYear: DataTypes.STRING, + publishedDate: DataTypes.STRING, + publisher: DataTypes.STRING, + description: DataTypes.TEXT, + isbn: DataTypes.STRING, + asin: DataTypes.STRING, + language: DataTypes.STRING, + explicit: DataTypes.BOOLEAN, + abridged: DataTypes.BOOLEAN, + coverPath: DataTypes.STRING, + duration: DataTypes.FLOAT, - narrators: DataTypes.JSON, - audioFiles: DataTypes.JSON, - ebookFile: DataTypes.JSON, - chapters: DataTypes.JSON, - tags: DataTypes.JSON, - genres: DataTypes.JSON - }, { - sequelize, - modelName: 'book', - indexes: [ - { - fields: [{ - name: 'title', - collate: 'NOCASE' - }] - }, - // { - // fields: [{ - // name: 'titleIgnorePrefix', - // collate: 'NOCASE' - // }] - // }, - { - fields: ['publishedYear'] - }, - // { - // fields: ['duration'] - // } - ] - }) + narrators: DataTypes.JSON, + audioFiles: DataTypes.JSON, + ebookFile: DataTypes.JSON, + chapters: DataTypes.JSON, + tags: DataTypes.JSON, + genres: DataTypes.JSON + }, + { + sequelize, + modelName: 'book', + indexes: [ + { + fields: [ + { + name: 'title', + collate: 'NOCASE' + } + ] + }, + // { + // fields: [{ + // name: 'titleIgnorePrefix', + // collate: 'NOCASE' + // }] + // }, + { + fields: ['publishedYear'] + } + // { + // fields: ['duration'] + // } + ] + } + ) } } -module.exports = Book \ No newline at end of file +module.exports = Book diff --git a/server/models/BookAuthor.js b/server/models/BookAuthor.js index 671e9470..45a84f1f 100644 --- a/server/models/BookAuthor.js +++ b/server/models/BookAuthor.js @@ -25,21 +25,24 @@ class BookAuthor extends Model { /** * Initialize model - * @param {import('../Database').sequelize} sequelize + * @param {import('../Database').sequelize} sequelize */ static init(sequelize) { - super.init({ - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true + super.init( + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + } + }, + { + sequelize, + modelName: 'bookAuthor', + timestamps: true, + updatedAt: false } - }, { - sequelize, - modelName: 'bookAuthor', - timestamps: true, - updatedAt: false - }) + ) // Super Many-to-Many // ref: https://sequelize.org/docs/v6/advanced-association-concepts/advanced-many-to-many/#the-best-of-both-worlds-the-super-many-to-many-relationship @@ -58,4 +61,4 @@ class BookAuthor extends Model { BookAuthor.belongsTo(author) } } -module.exports = BookAuthor \ No newline at end of file +module.exports = BookAuthor diff --git a/server/models/BookSeries.js b/server/models/BookSeries.js index fe2a07a5..fad54718 100644 --- a/server/models/BookSeries.js +++ b/server/models/BookSeries.js @@ -27,22 +27,25 @@ class BookSeries extends Model { /** * Initialize model - * @param {import('../Database').sequelize} sequelize + * @param {import('../Database').sequelize} sequelize */ static init(sequelize) { - super.init({ - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true + super.init( + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + sequence: DataTypes.STRING }, - sequence: DataTypes.STRING - }, { - sequelize, - modelName: 'bookSeries', - timestamps: true, - updatedAt: false - }) + { + sequelize, + modelName: 'bookSeries', + timestamps: true, + updatedAt: false + } + ) // Super Many-to-Many // ref: https://sequelize.org/docs/v6/advanced-association-concepts/advanced-many-to-many/#the-best-of-both-worlds-the-super-many-to-many-relationship @@ -62,4 +65,4 @@ class BookSeries extends Model { } } -module.exports = BookSeries \ No newline at end of file +module.exports = BookSeries diff --git a/server/models/Collection.js b/server/models/Collection.js index 9d3a8e0a..5fa0310d 100644 --- a/server/models/Collection.js +++ b/server/models/Collection.js @@ -2,7 +2,6 @@ const { DataTypes, Model, Sequelize } = require('sequelize') const oldCollection = require('../objects/Collection') - class Collection extends Model { constructor(values, options) { super(values, options) @@ -20,27 +19,13 @@ class Collection extends Model { /** @type {Date} */ this.createdAt } - /** - * Get all old collections - * @returns {Promise} - */ - static async getOldCollections() { - const collections = await this.findAll({ - include: { - model: this.sequelize.models.book, - include: this.sequelize.models.libraryItem - }, - order: [[this.sequelize.models.book, this.sequelize.models.collectionBook, 'order', 'ASC']] - }) - return collections.map(c => this.getOldCollection(c)) - } /** * Get all old collections toJSONExpanded, items filtered for user permissions - * @param {[oldUser]} user - * @param {[string]} libraryId - * @param {[string[]]} include - * @returns {Promise} oldCollection.toJSONExpanded + * @param {oldUser} [user] + * @param {string} [libraryId] + * @param {string[]} [include] + * @returns {Promise} oldCollection.toJSONExpanded */ static async getOldCollectionsJsonExpanded(user, libraryId, include) { let collectionWhere = null @@ -78,8 +63,7 @@ class Collection extends Model { through: { attributes: ['sequence'] } - }, - + } ] }, ...collectionIncludes @@ -87,11 +71,84 @@ class Collection extends Model { order: [[this.sequelize.models.book, this.sequelize.models.collectionBook, 'order', 'ASC']] }) // TODO: Handle user permission restrictions on initial query - return collections.map(c => { - const oldCollection = this.getOldCollection(c) + return collections + .map((c) => { + const oldCollection = this.getOldCollection(c) - // Filter books using user permissions - const books = c.books?.filter(b => { + // Filter books using user permissions + const books = + c.books?.filter((b) => { + if (user) { + 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 && oldCollection.books.length) { + return null + } + + const collectionExpanded = oldCollection.toJSONExpanded(libraryItems) + + // Map feed if found + if (c.feeds?.length) { + collectionExpanded.rssFeed = this.sequelize.models.feed.getOldFeed(c.feeds[0]) + } + + return collectionExpanded + }) + .filter((c) => c) + } + + /** + * Get old collection toJSONExpanded, items filtered for user permissions + * @param {oldUser} [user] + * @param {string[]} [include] + * @returns {Promise} oldCollection.toJSONExpanded + */ + 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')] + })) || [] + + const oldCollection = this.sequelize.models.collection.getOldCollection(this) + + // 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 @@ -103,77 +160,8 @@ 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) { - return null - } - - const collectionExpanded = oldCollection.toJSONExpanded(libraryItems) - - // Map feed if found - if (c.feeds?.length) { - collectionExpanded.rssFeed = this.sequelize.models.feed.getOldFeed(c.feeds[0]) - } - - return collectionExpanded - }).filter(c => c) - } - - /** - * Get old collection toJSONExpanded, items filtered for user permissions - * @param {[oldUser]} user - * @param {[string[]]} include - * @returns {Promise} oldCollection.toJSONExpanded - */ - 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')] - }) || [] - - const oldCollection = this.sequelize.models.collection.getOldCollection(this) - - // 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 - } - } - return true - }) || [] - // Map to library items - const libraryItems = books.map(b => { + const libraryItems = books.map((b) => { const libraryItem = b.libraryItem delete b.libraryItem libraryItem.media = b @@ -199,11 +187,11 @@ class Collection extends Model { /** * Get old collection from Collection - * @param {Collection} collectionExpanded + * @param {Collection} collectionExpanded * @returns {oldCollection} */ static getOldCollection(collectionExpanded) { - const libraryItemIds = collectionExpanded.books?.map(b => b.libraryItem?.id || null).filter(lid => lid) || [] + const libraryItemIds = collectionExpanded.books?.map((b) => b.libraryItem?.id || null).filter((lid) => lid) || [] return new oldCollection({ id: collectionExpanded.id, libraryId: collectionExpanded.libraryId, @@ -215,6 +203,11 @@ class Collection extends Model { }) } + /** + * + * @param {oldCollection} oldCollection + * @returns {Promise} + */ static createFromOld(oldCollection) { const collection = this.getFromOld(oldCollection) return this.create(collection) @@ -239,7 +232,7 @@ class Collection extends Model { /** * Get old collection by id - * @param {string} collectionId + * @param {string} collectionId * @returns {Promise} returns null if not found */ static async getOldById(collectionId) { @@ -260,34 +253,34 @@ class Collection extends Model { * @returns {Promise} */ async getOld() { - this.books = await this.getBooks({ - include: [ - { - model: this.sequelize.models.libraryItem - }, - { - model: this.sequelize.models.author, - through: { - attributes: [] + 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'] + } } - }, - { - model: this.sequelize.models.series, - through: { - attributes: ['sequence'] - } - }, - - ], - order: [Sequelize.literal('`collectionBook.order` ASC')] - }) || [] + ], + order: [Sequelize.literal('`collectionBook.order` ASC')] + })) || [] return this.sequelize.models.collection.getOldCollection(this) } /** * Remove all collections belonging to library - * @param {string} libraryId + * @param {string} libraryId * @returns {Promise} number of collections destroyed */ static async removeAllForLibrary(libraryId) { @@ -299,38 +292,26 @@ class Collection extends Model { }) } - static async getAllForBook(bookId) { - const collections = await this.findAll({ - include: { - model: this.sequelize.models.book, - where: { - id: bookId - }, - required: true, - include: this.sequelize.models.libraryItem - }, - order: [[this.sequelize.models.book, this.sequelize.models.collectionBook, 'order', 'ASC']] - }) - return collections.map(c => this.getOldCollection(c)) - } - /** * Initialize model - * @param {import('../Database').sequelize} sequelize + * @param {import('../Database').sequelize} sequelize */ static init(sequelize) { - super.init({ - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true + super.init( + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + name: DataTypes.STRING, + description: DataTypes.TEXT }, - name: DataTypes.STRING, - description: DataTypes.TEXT - }, { - sequelize, - modelName: 'collection' - }) + { + sequelize, + modelName: 'collection' + } + ) const { library } = sequelize.models @@ -339,4 +320,4 @@ class Collection extends Model { } } -module.exports = Collection \ No newline at end of file +module.exports = Collection diff --git a/server/models/CollectionBook.js b/server/models/CollectionBook.js index aab3a1d3..e04da3b2 100644 --- a/server/models/CollectionBook.js +++ b/server/models/CollectionBook.js @@ -26,19 +26,22 @@ class CollectionBook extends Model { } static init(sequelize) { - super.init({ - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true + super.init( + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + order: DataTypes.INTEGER }, - order: DataTypes.INTEGER - }, { - sequelize, - timestamps: true, - updatedAt: false, - modelName: 'collectionBook' - }) + { + sequelize, + timestamps: true, + updatedAt: false, + modelName: 'collectionBook' + } + ) // Super Many-to-Many // ref: https://sequelize.org/docs/v6/advanced-association-concepts/advanced-many-to-many/#the-best-of-both-worlds-the-super-many-to-many-relationship @@ -58,4 +61,4 @@ class CollectionBook extends Model { } } -module.exports = CollectionBook \ No newline at end of file +module.exports = CollectionBook diff --git a/server/models/Device.js b/server/models/Device.js index 24cd2276..896967e4 100644 --- a/server/models/Device.js +++ b/server/models/Device.js @@ -114,26 +114,29 @@ class Device extends Model { /** * Initialize model - * @param {import('../Database').sequelize} sequelize + * @param {import('../Database').sequelize} sequelize */ static init(sequelize) { - super.init({ - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true + super.init( + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + deviceId: DataTypes.STRING, + clientName: DataTypes.STRING, // e.g. Abs Web, Abs Android + clientVersion: DataTypes.STRING, // e.g. Server version or mobile version + ipAddress: DataTypes.STRING, + deviceName: DataTypes.STRING, // e.g. Windows 10 Chrome, Google Pixel 6, Apple iPhone 10,3 + deviceVersion: DataTypes.STRING, // e.g. Browser version or Android SDK + extraData: DataTypes.JSON }, - deviceId: DataTypes.STRING, - clientName: DataTypes.STRING, // e.g. Abs Web, Abs Android - clientVersion: DataTypes.STRING, // e.g. Server version or mobile version - ipAddress: DataTypes.STRING, - deviceName: DataTypes.STRING, // e.g. Windows 10 Chrome, Google Pixel 6, Apple iPhone 10,3 - deviceVersion: DataTypes.STRING, // e.g. Browser version or Android SDK - extraData: DataTypes.JSON - }, { - sequelize, - modelName: 'device' - }) + { + sequelize, + modelName: 'device' + } + ) const { user } = sequelize.models @@ -144,4 +147,4 @@ class Device extends Model { } } -module.exports = Device \ No newline at end of file +module.exports = Device diff --git a/server/models/Feed.js b/server/models/Feed.js index d8c5a2a7..72321da9 100644 --- a/server/models/Feed.js +++ b/server/models/Feed.js @@ -58,7 +58,7 @@ class Feed extends Model { model: this.sequelize.models.feedEpisode } }) - return feeds.map(f => this.getOldFeed(f)) + return feeds.map((f) => this.getOldFeed(f)) } /** @@ -117,7 +117,7 @@ class Feed extends Model { entityType: 'libraryItem' } }) - return feeds.map(f => f.entityId).filter(f => f) || [] + return feeds.map((f) => f.entityId).filter((f) => f) || [] } /** @@ -179,7 +179,7 @@ class Feed extends Model { // Remove and update existing feed episodes for (const feedEpisode of existingFeed.feedEpisodes) { - const oldFeedEpisode = oldFeedEpisodes.find(ep => ep.id === feedEpisode.id) + const oldFeedEpisode = oldFeedEpisodes.find((ep) => ep.id === feedEpisode.id) // Episode removed if (!oldFeedEpisode) { feedEpisode.destroy() @@ -200,7 +200,7 @@ class Feed extends Model { // Add new feed episodes for (const episode of oldFeedEpisodes) { - if (!existingFeed.feedEpisodes.some(fe => fe.id === episode.id)) { + if (!existingFeed.feedEpisodes.some((fe) => fe.id === episode.id)) { await this.sequelize.models.feedEpisode.createFromOld(feedObj.id, episode) hasUpdates = true } @@ -258,41 +258,44 @@ class Feed extends Model { /** * Initialize model - * + * * Polymorphic association: Feeds can be created from LibraryItem, Collection, Playlist or Series * @see https://sequelize.org/docs/v6/advanced-association-concepts/polymorphic-associations/ - * - * @param {import('../Database').sequelize} sequelize + * + * @param {import('../Database').sequelize} sequelize */ static init(sequelize) { - super.init({ - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true + super.init( + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + slug: DataTypes.STRING, + entityType: DataTypes.STRING, + entityId: DataTypes.UUIDV4, + entityUpdatedAt: DataTypes.DATE, + serverAddress: DataTypes.STRING, + feedURL: DataTypes.STRING, + imageURL: DataTypes.STRING, + siteURL: DataTypes.STRING, + title: DataTypes.STRING, + description: DataTypes.TEXT, + author: DataTypes.STRING, + podcastType: DataTypes.STRING, + language: DataTypes.STRING, + ownerName: DataTypes.STRING, + ownerEmail: DataTypes.STRING, + explicit: DataTypes.BOOLEAN, + preventIndexing: DataTypes.BOOLEAN, + coverPath: DataTypes.STRING }, - slug: DataTypes.STRING, - entityType: DataTypes.STRING, - entityId: DataTypes.UUIDV4, - entityUpdatedAt: DataTypes.DATE, - serverAddress: DataTypes.STRING, - feedURL: DataTypes.STRING, - imageURL: DataTypes.STRING, - siteURL: DataTypes.STRING, - title: DataTypes.STRING, - description: DataTypes.TEXT, - author: DataTypes.STRING, - podcastType: DataTypes.STRING, - language: DataTypes.STRING, - ownerName: DataTypes.STRING, - ownerEmail: DataTypes.STRING, - explicit: DataTypes.BOOLEAN, - preventIndexing: DataTypes.BOOLEAN, - coverPath: DataTypes.STRING - }, { - sequelize, - modelName: 'feed' - }) + { + sequelize, + modelName: 'feed' + } + ) const { user, libraryItem, collection, series, playlist } = sequelize.models @@ -335,7 +338,7 @@ class Feed extends Model { }) Feed.belongsTo(playlist, { foreignKey: 'entityId', constraints: false }) - Feed.addHook('afterFind', findResult => { + Feed.addHook('afterFind', (findResult) => { if (!findResult) return if (!Array.isArray(findResult)) findResult = [findResult] @@ -368,4 +371,4 @@ class Feed extends Model { } } -module.exports = Feed \ No newline at end of file +module.exports = Feed diff --git a/server/models/FeedEpisode.js b/server/models/FeedEpisode.js index 01659255..442cc165 100644 --- a/server/models/FeedEpisode.js +++ b/server/models/FeedEpisode.js @@ -65,9 +65,9 @@ class FeedEpisode extends Model { /** * Create feed episode from old model - * - * @param {string} feedId - * @param {Object} oldFeedEpisode + * + * @param {string} feedId + * @param {Object} oldFeedEpisode * @returns {Promise} */ static createFromOld(feedId, oldFeedEpisode) { @@ -98,33 +98,36 @@ class FeedEpisode extends Model { /** * Initialize model - * @param {import('../Database').sequelize} sequelize + * @param {import('../Database').sequelize} sequelize */ static init(sequelize) { - super.init({ - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true + super.init( + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + title: DataTypes.STRING, + author: DataTypes.STRING, + description: DataTypes.TEXT, + siteURL: DataTypes.STRING, + enclosureURL: DataTypes.STRING, + enclosureType: DataTypes.STRING, + enclosureSize: DataTypes.BIGINT, + pubDate: DataTypes.STRING, + season: DataTypes.STRING, + episode: DataTypes.STRING, + episodeType: DataTypes.STRING, + duration: DataTypes.FLOAT, + filePath: DataTypes.STRING, + explicit: DataTypes.BOOLEAN }, - title: DataTypes.STRING, - author: DataTypes.STRING, - description: DataTypes.TEXT, - siteURL: DataTypes.STRING, - enclosureURL: DataTypes.STRING, - enclosureType: DataTypes.STRING, - enclosureSize: DataTypes.BIGINT, - pubDate: DataTypes.STRING, - season: DataTypes.STRING, - episode: DataTypes.STRING, - episodeType: DataTypes.STRING, - duration: DataTypes.FLOAT, - filePath: DataTypes.STRING, - explicit: DataTypes.BOOLEAN - }, { - sequelize, - modelName: 'feedEpisode' - }) + { + sequelize, + modelName: 'feedEpisode' + } + ) const { feed } = sequelize.models @@ -135,4 +138,4 @@ class FeedEpisode extends Model { } } -module.exports = FeedEpisode \ No newline at end of file +module.exports = FeedEpisode diff --git a/server/models/Library.js b/server/models/Library.js index 49b54d68..103d14b6 100644 --- a/server/models/Library.js +++ b/server/models/Library.js @@ -10,7 +10,7 @@ const oldLibrary = require('../objects/Library') * @property {boolean} skipMatchingMediaWithIsbn * @property {string} autoScanCronExpression * @property {boolean} audiobooksOnly - * @property {boolean} hideSingleBookSeries Do not show series that only have 1 book + * @property {boolean} hideSingleBookSeries Do not show series that only have 1 book * @property {boolean} onlyShowLaterBooksInContinueSeries Skip showing books that are earlier than the max sequence read * @property {string[]} metadataPrecedence */ @@ -54,16 +54,16 @@ class Library extends Model { include: this.sequelize.models.libraryFolder, order: [['displayOrder', 'ASC']] }) - return libraries.map(lib => this.getOldLibrary(lib)) + return libraries.map((lib) => this.getOldLibrary(lib)) } /** * Convert expanded Library to oldLibrary - * @param {Library} libraryExpanded + * @param {Library} libraryExpanded * @returns {Promise} */ static getOldLibrary(libraryExpanded) { - const folders = libraryExpanded.libraryFolders.map(folder => { + const folders = libraryExpanded.libraryFolders.map((folder) => { return { id: folder.id, fullPath: folder.path, @@ -90,13 +90,13 @@ class Library extends Model { } /** - * @param {object} oldLibrary + * @param {object} oldLibrary * @returns {Library|null} */ static async createFromOld(oldLibrary) { const library = this.getFromOld(oldLibrary) - library.libraryFolders = oldLibrary.folders.map(folder => { + library.libraryFolders = oldLibrary.folders.map((folder) => { return { id: folder.id, path: folder.fullPath @@ -113,8 +113,8 @@ class Library extends Model { /** * Update library and library folders - * @param {object} oldLibrary - * @returns + * @param {object} oldLibrary + * @returns */ static async updateFromOld(oldLibrary) { const existingLibrary = await this.findByPk(oldLibrary.id, { @@ -127,7 +127,7 @@ class Library extends Model { const library = this.getFromOld(oldLibrary) - const libraryFolders = oldLibrary.folders.map(folder => { + const libraryFolders = oldLibrary.folders.map((folder) => { return { id: folder.id, path: folder.fullPath, @@ -135,7 +135,7 @@ class Library extends Model { } }) for (const libraryFolder of libraryFolders) { - const existingLibraryFolder = existingLibrary.libraryFolders.find(lf => lf.id === libraryFolder.id) + const existingLibraryFolder = existingLibrary.libraryFolders.find((lf) => lf.id === libraryFolder.id) if (!existingLibraryFolder) { await this.sequelize.models.libraryFolder.create(libraryFolder) } else if (existingLibraryFolder.path !== libraryFolder.path) { @@ -143,7 +143,7 @@ class Library extends Model { } } - const libraryFoldersRemoved = existingLibrary.libraryFolders.filter(lf => !libraryFolders.some(_lf => _lf.id === lf.id)) + const libraryFoldersRemoved = existingLibrary.libraryFolders.filter((lf) => !libraryFolders.some((_lf) => _lf.id === lf.id)) for (const existingLibraryFolder of libraryFoldersRemoved) { await existingLibraryFolder.destroy() } @@ -177,8 +177,8 @@ class Library extends Model { /** * Destroy library by id - * @param {string} libraryId - * @returns + * @param {string} libraryId + * @returns */ static removeById(libraryId) { return this.destroy({ @@ -197,12 +197,12 @@ class Library extends Model { attributes: ['id', 'displayOrder'], order: [['displayOrder', 'ASC']] }) - return libraries.map(l => l.id) + return libraries.map((l) => l.id) } /** * Find Library by primary key & return oldLibrary - * @param {string} libraryId + * @param {string} libraryId * @returns {Promise} Returns null if not found */ static async getOldById(libraryId) { @@ -244,29 +244,32 @@ class Library extends Model { /** * Initialize model - * @param {import('../Database').sequelize} sequelize + * @param {import('../Database').sequelize} sequelize */ static init(sequelize) { - super.init({ - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true + super.init( + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + name: DataTypes.STRING, + displayOrder: DataTypes.INTEGER, + icon: DataTypes.STRING, + mediaType: DataTypes.STRING, + provider: DataTypes.STRING, + lastScan: DataTypes.DATE, + lastScanVersion: DataTypes.STRING, + settings: DataTypes.JSON, + extraData: DataTypes.JSON }, - name: DataTypes.STRING, - displayOrder: DataTypes.INTEGER, - icon: DataTypes.STRING, - mediaType: DataTypes.STRING, - provider: DataTypes.STRING, - lastScan: DataTypes.DATE, - lastScanVersion: DataTypes.STRING, - settings: DataTypes.JSON, - extraData: DataTypes.JSON - }, { - sequelize, - modelName: 'library' - }) + { + sequelize, + modelName: 'library' + } + ) } } -module.exports = Library \ No newline at end of file +module.exports = Library diff --git a/server/models/LibraryFolder.js b/server/models/LibraryFolder.js index 6ae7a8ac..db607547 100644 --- a/server/models/LibraryFolder.js +++ b/server/models/LibraryFolder.js @@ -16,33 +16,25 @@ class LibraryFolder extends Model { this.updatedAt } - /** - * Gets all library folder path strings - * @returns {Promise} array of library folder paths - */ - static async getAllLibraryFolderPaths() { - const libraryFolders = await this.findAll({ - attributes: ['path'] - }) - return libraryFolders.map(l => l.path) - } - /** * Initialize model - * @param {import('../Database').sequelize} sequelize + * @param {import('../Database').sequelize} sequelize */ static init(sequelize) { - super.init({ - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true + super.init( + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + path: DataTypes.STRING }, - path: DataTypes.STRING - }, { - sequelize, - modelName: 'libraryFolder' - }) + { + sequelize, + modelName: 'libraryFolder' + } + ) const { library } = sequelize.models library.hasMany(LibraryFolder, { @@ -52,4 +44,4 @@ class LibraryFolder extends Model { } } -module.exports = LibraryFolder \ No newline at end of file +module.exports = LibraryFolder diff --git a/server/models/LibraryItem.js b/server/models/LibraryItem.js index 5a35a5d6..2eccee19 100644 --- a/server/models/LibraryItem.js +++ b/server/models/LibraryItem.js @@ -21,8 +21,8 @@ const Podcast = require('./Podcast') /** * @typedef LibraryItemExpandedProperties - * @property {Book.BookExpanded|Podcast.PodcastExpanded} media - * + * @property {Book.BookExpanded|Podcast.PodcastExpanded} media + * * @typedef {LibraryItem & LibraryItemExpandedProperties} LibraryItemExpanded */ @@ -77,7 +77,7 @@ class LibraryItem extends Model { /** * Gets library items partially expanded, not including podcast episodes * @todo temporary solution - * + * * @param {number} offset * @param {number} limit * @returns {Promise} LibraryItem @@ -154,13 +154,13 @@ class LibraryItem extends Model { } ] }) - return libraryItems.map(ti => this.getOldLibraryItem(ti)) + return libraryItems.map((ti) => this.getOldLibraryItem(ti)) } /** * Convert an expanded LibraryItem into an old library item - * - * @param {Model} libraryItemExpanded + * + * @param {Model} libraryItemExpanded * @returns {oldLibraryItem} */ static getOldLibraryItem(libraryItemExpanded) { @@ -231,8 +231,8 @@ class LibraryItem extends Model { /** * Updates libraryItem, book, authors and series from old library item - * - * @param {oldLibraryItem} oldLibraryItem + * + * @param {oldLibraryItem} oldLibraryItem * @returns {Promise} true if updates were made */ static async fullUpdateFromOld(oldLibraryItem) { @@ -280,14 +280,14 @@ class LibraryItem extends Model { for (const existingPodcastEpisode of existingPodcastEpisodes) { // Episode was removed - if (!updatedPodcastEpisodes.some(ep => ep.id === existingPodcastEpisode.id)) { + if (!updatedPodcastEpisodes.some((ep) => ep.id === existingPodcastEpisode.id)) { Logger.debug(`[LibraryItem] "${libraryItemExpanded.media.title}" episode "${existingPodcastEpisode.title}" was removed`) await existingPodcastEpisode.destroy() hasUpdates = true } } for (const updatedPodcastEpisode of updatedPodcastEpisodes) { - const existingEpisodeMatch = existingPodcastEpisodes.find(ep => ep.id === updatedPodcastEpisode.id) + const existingEpisodeMatch = existingPodcastEpisodes.find((ep) => ep.id === updatedPodcastEpisode.id) if (!existingEpisodeMatch) { Logger.debug(`[LibraryItem] "${libraryItemExpanded.media.title}" episode "${updatedPodcastEpisode.title}" was added`) await this.sequelize.models.podcastEpisode.createFromOld(updatedPodcastEpisode) @@ -316,12 +316,12 @@ class LibraryItem extends Model { const existingAuthors = libraryItemExpanded.media.authors || [] const existingSeriesAll = libraryItemExpanded.media.series || [] const updatedAuthors = oldLibraryItem.media.metadata.authors || [] - const uniqueUpdatedAuthors = updatedAuthors.filter((au, idx) => updatedAuthors.findIndex(a => a.id === au.id) === idx) + const uniqueUpdatedAuthors = updatedAuthors.filter((au, idx) => updatedAuthors.findIndex((a) => a.id === au.id) === idx) const updatedSeriesAll = oldLibraryItem.media.metadata.series || [] for (const existingAuthor of existingAuthors) { // Author was removed from Book - if (!uniqueUpdatedAuthors.some(au => au.id === existingAuthor.id)) { + if (!uniqueUpdatedAuthors.some((au) => au.id === existingAuthor.id)) { Logger.debug(`[LibraryItem] "${libraryItemExpanded.media.title}" author "${existingAuthor.name}" was removed`) await this.sequelize.models.bookAuthor.removeByIds(existingAuthor.id, libraryItemExpanded.media.id) hasUpdates = true @@ -329,7 +329,7 @@ class LibraryItem extends Model { } for (const updatedAuthor of uniqueUpdatedAuthors) { // Author was added - if (!existingAuthors.some(au => au.id === updatedAuthor.id)) { + if (!existingAuthors.some((au) => au.id === updatedAuthor.id)) { Logger.debug(`[LibraryItem] "${libraryItemExpanded.media.title}" author "${updatedAuthor.name}" was added`) await this.sequelize.models.bookAuthor.create({ authorId: updatedAuthor.id, bookId: libraryItemExpanded.media.id }) hasUpdates = true @@ -337,7 +337,7 @@ class LibraryItem extends Model { } for (const existingSeries of existingSeriesAll) { // Series was removed - if (!updatedSeriesAll.some(se => se.id === existingSeries.id)) { + if (!updatedSeriesAll.some((se) => se.id === existingSeries.id)) { Logger.debug(`[LibraryItem] "${libraryItemExpanded.media.title}" series "${existingSeries.name}" was removed`) await this.sequelize.models.bookSeries.removeByIds(existingSeries.id, libraryItemExpanded.media.id) hasUpdates = true @@ -345,7 +345,7 @@ class LibraryItem extends Model { } for (const updatedSeries of updatedSeriesAll) { // Series was added/updated - const existingSeriesMatch = existingSeriesAll.find(se => se.id === updatedSeries.id) + const existingSeriesMatch = existingSeriesAll.find((se) => se.id === updatedSeries.id) if (!existingSeriesMatch) { Logger.debug(`[LibraryItem] "${libraryItemExpanded.media.title}" series "${updatedSeries.name}" was added`) await this.sequelize.models.bookSeries.create({ seriesId: updatedSeries.id, bookId: libraryItemExpanded.media.id, sequence: updatedSeries.sequence }) @@ -420,7 +420,7 @@ class LibraryItem extends Model { lastScanVersion: oldLibraryItem.scanVersion, libraryId: oldLibraryItem.libraryId, libraryFolderId: oldLibraryItem.folderId, - libraryFiles: oldLibraryItem.libraryFiles?.map(lf => lf.toJSON()) || [], + libraryFiles: oldLibraryItem.libraryFiles?.map((lf) => lf.toJSON()) || [], extraData } } @@ -435,8 +435,8 @@ class LibraryItem extends Model { } /** - * - * @param {string} libraryItemId + * + * @param {string} libraryItemId * @returns {Promise} */ static async getExpandedById(libraryItemId) { @@ -485,7 +485,7 @@ class LibraryItem extends Model { /** * Get old library item by id - * @param {string} libraryItemId + * @param {string} libraryItemId * @returns {oldLibraryItem} */ static async getOldById(libraryItemId) { @@ -534,9 +534,9 @@ class LibraryItem extends Model { /** * Get library items using filter and sort - * @param {oldLibrary} library - * @param {oldUser} user - * @param {object} options + * @param {oldLibrary} library + * @param {oldUser} user + * @param {object} options * @returns {object} { libraryItems:oldLibraryItem[], count:number } */ static async getByFilterAndSort(library, user, options) { @@ -545,7 +545,7 @@ class LibraryItem extends Model { Logger.debug(`Loaded ${libraryItems.length} of ${count} items for libary page in ${((Date.now() - start) / 1000).toFixed(2)}s`) return { - libraryItems: libraryItems.map(li => { + libraryItems: libraryItems.map((li) => { const oldLibraryItem = this.getOldLibraryItem(li).toJSONMinified() if (li.collapsedSeries) { oldLibraryItem.collapsedSeries = li.collapsedSeries @@ -574,10 +574,10 @@ class LibraryItem extends Model { /** * Get home page data personalized shelves - * @param {oldLibrary} library - * @param {oldUser} user - * @param {string[]} include - * @param {number} limit + * @param {oldLibrary} library + * @param {oldUser} user + * @param {string[]} include + * @param {number} limit * @returns {object[]} array of shelf objects */ static async getPersonalizedShelves(library, user, include, limit) { @@ -588,8 +588,8 @@ class LibraryItem extends Model { // "Continue Listening" shelf const itemsInProgressPayload = await libraryFilters.getMediaItemsInProgress(library, user, include, limit, false) if (itemsInProgressPayload.items.length) { - const ebookOnlyItemsInProgress = itemsInProgressPayload.items.filter(li => li.media.isEBookOnly) - const audioOnlyItemsInProgress = itemsInProgressPayload.items.filter(li => !li.media.isEBookOnly) + const ebookOnlyItemsInProgress = itemsInProgressPayload.items.filter((li) => li.media.isEBookOnly) + const audioOnlyItemsInProgress = itemsInProgressPayload.items.filter((li) => !li.media.isEBookOnly) shelves.push({ id: 'continue-listening', @@ -697,8 +697,8 @@ class LibraryItem extends Model { // "Listen Again" shelf const mediaFinishedPayload = await libraryFilters.getMediaFinished(library, user, include, limit) if (mediaFinishedPayload.items.length) { - const ebookOnlyItemsInProgress = mediaFinishedPayload.items.filter(li => li.media.isEBookOnly) - const audioOnlyItemsInProgress = mediaFinishedPayload.items.filter(li => !li.media.isEBookOnly) + const ebookOnlyItemsInProgress = mediaFinishedPayload.items.filter((li) => li.media.isEBookOnly) + const audioOnlyItemsInProgress = mediaFinishedPayload.items.filter((li) => !li.media.isEBookOnly) shelves.push({ id: 'listen-again', @@ -748,27 +748,27 @@ class LibraryItem extends Model { /** * Get book library items for author, optional use user permissions * @param {oldAuthor} author - * @param {[oldUser]} user + * @param {[oldUser]} user * @returns {Promise} */ static async getForAuthor(author, user = null) { const { libraryItems } = await libraryFilters.getLibraryItemsForAuthor(author, user, undefined, undefined) - return libraryItems.map(li => this.getOldLibraryItem(li)) + return libraryItems.map((li) => this.getOldLibraryItem(li)) } /** * Get book library items in a collection - * @param {oldCollection} collection + * @param {oldCollection} collection * @returns {Promise} */ static async getForCollection(collection) { const libraryItems = await libraryFilters.getLibraryItemsForCollection(collection) - return libraryItems.map(li => this.getOldLibraryItem(li)) + return libraryItems.map((li) => this.getOldLibraryItem(li)) } /** * Check if library item exists - * @param {string} libraryItemId + * @param {string} libraryItemId * @returns {Promise} */ static async checkExistsById(libraryItemId) { @@ -776,8 +776,8 @@ class LibraryItem extends Model { } /** - * - * @param {import('sequelize').WhereOptions} where + * + * @param {import('sequelize').WhereOptions} where * @param {import('sequelize').BindOrReplacements} replacements * @returns {Object} oldLibraryItem */ @@ -822,8 +822,8 @@ class LibraryItem extends Model { } /** - * - * @param {import('sequelize').FindOptions} options + * + * @param {import('sequelize').FindOptions} options * @returns {Promise} */ getMedia(options) { @@ -833,7 +833,7 @@ class LibraryItem extends Model { } /** - * + * * @returns {Promise} */ getMediaExpanded() { @@ -870,7 +870,7 @@ class LibraryItem extends Model { } /** - * + * * @returns {Promise} */ async saveMetadataFile() { @@ -887,18 +887,18 @@ class LibraryItem extends Model { const metadataFilePath = Path.join(metadataPath, `metadata.${global.ServerSettings.metadataFileFormat}`) // Expanded with series, authors, podcastEpisodes - const mediaExpanded = this.media || await this.getMediaExpanded() + const mediaExpanded = this.media || (await this.getMediaExpanded()) let jsonObject = {} if (this.mediaType === 'book') { jsonObject = { tags: mediaExpanded.tags || [], - chapters: mediaExpanded.chapters?.map(c => ({ ...c })) || [], + chapters: mediaExpanded.chapters?.map((c) => ({ ...c })) || [], title: mediaExpanded.title, subtitle: mediaExpanded.subtitle, - authors: mediaExpanded.authors.map(a => a.name), + authors: mediaExpanded.authors.map((a) => a.name), narrators: mediaExpanded.narrators, - series: mediaExpanded.series.map(se => { + series: mediaExpanded.series.map((se) => { const sequence = se.bookSeries?.sequence || '' if (!sequence) return se.name return `${se.name} #${sequence}` @@ -934,96 +934,101 @@ class LibraryItem extends Model { } } - - return fsExtra.writeFile(metadataFilePath, JSON.stringify(jsonObject, null, 2)).then(async () => { - // Add metadata.json to libraryFiles array if it is new - let metadataLibraryFile = this.libraryFiles.find(lf => lf.metadata.path === filePathToPOSIX(metadataFilePath)) - if (storeMetadataWithItem) { - if (!metadataLibraryFile) { - const newLibraryFile = new LibraryFile() - await newLibraryFile.setDataFromPath(metadataFilePath, `metadata.json`) - metadataLibraryFile = newLibraryFile.toJSON() - this.libraryFiles.push(metadataLibraryFile) - } else { - const fileTimestamps = await getFileTimestampsWithIno(metadataFilePath) - if (fileTimestamps) { - metadataLibraryFile.metadata.mtimeMs = fileTimestamps.mtimeMs - metadataLibraryFile.metadata.ctimeMs = fileTimestamps.ctimeMs - metadataLibraryFile.metadata.size = fileTimestamps.size - metadataLibraryFile.ino = fileTimestamps.ino + return fsExtra + .writeFile(metadataFilePath, JSON.stringify(jsonObject, null, 2)) + .then(async () => { + // Add metadata.json to libraryFiles array if it is new + let metadataLibraryFile = this.libraryFiles.find((lf) => lf.metadata.path === filePathToPOSIX(metadataFilePath)) + if (storeMetadataWithItem) { + if (!metadataLibraryFile) { + const newLibraryFile = new LibraryFile() + await newLibraryFile.setDataFromPath(metadataFilePath, `metadata.json`) + metadataLibraryFile = newLibraryFile.toJSON() + this.libraryFiles.push(metadataLibraryFile) + } else { + const fileTimestamps = await getFileTimestampsWithIno(metadataFilePath) + if (fileTimestamps) { + metadataLibraryFile.metadata.mtimeMs = fileTimestamps.mtimeMs + metadataLibraryFile.metadata.ctimeMs = fileTimestamps.ctimeMs + metadataLibraryFile.metadata.size = fileTimestamps.size + metadataLibraryFile.ino = fileTimestamps.ino + } + } + const libraryItemDirTimestamps = await getFileTimestampsWithIno(this.path) + if (libraryItemDirTimestamps) { + this.mtime = libraryItemDirTimestamps.mtimeMs + this.ctime = libraryItemDirTimestamps.ctimeMs + let size = 0 + this.libraryFiles.forEach((lf) => (size += !isNaN(lf.metadata.size) ? Number(lf.metadata.size) : 0)) + this.size = size + await this.save() } } - const libraryItemDirTimestamps = await getFileTimestampsWithIno(this.path) - if (libraryItemDirTimestamps) { - this.mtime = libraryItemDirTimestamps.mtimeMs - this.ctime = libraryItemDirTimestamps.ctimeMs - let size = 0 - this.libraryFiles.forEach((lf) => size += (!isNaN(lf.metadata.size) ? Number(lf.metadata.size) : 0)) - this.size = size - await this.save() - } - } - Logger.debug(`Success saving abmetadata to "${metadataFilePath}"`) + Logger.debug(`Success saving abmetadata to "${metadataFilePath}"`) - return metadataLibraryFile - }).catch((error) => { - Logger.error(`Failed to save json file at "${metadataFilePath}"`, error) - return null - }) + return metadataLibraryFile + }) + .catch((error) => { + Logger.error(`Failed to save json file at "${metadataFilePath}"`, error) + return null + }) } /** * Initialize model - * @param {import('../Database').sequelize} sequelize + * @param {import('../Database').sequelize} sequelize */ static init(sequelize) { - super.init({ - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true + super.init( + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + ino: DataTypes.STRING, + path: DataTypes.STRING, + relPath: DataTypes.STRING, + mediaId: DataTypes.UUIDV4, + mediaType: DataTypes.STRING, + isFile: DataTypes.BOOLEAN, + isMissing: DataTypes.BOOLEAN, + isInvalid: DataTypes.BOOLEAN, + mtime: DataTypes.DATE(6), + ctime: DataTypes.DATE(6), + birthtime: DataTypes.DATE(6), + size: DataTypes.BIGINT, + lastScan: DataTypes.DATE, + lastScanVersion: DataTypes.STRING, + libraryFiles: DataTypes.JSON, + extraData: DataTypes.JSON }, - ino: DataTypes.STRING, - path: DataTypes.STRING, - relPath: DataTypes.STRING, - mediaId: DataTypes.UUIDV4, - mediaType: DataTypes.STRING, - isFile: DataTypes.BOOLEAN, - isMissing: DataTypes.BOOLEAN, - isInvalid: DataTypes.BOOLEAN, - mtime: DataTypes.DATE(6), - ctime: DataTypes.DATE(6), - birthtime: DataTypes.DATE(6), - size: DataTypes.BIGINT, - lastScan: DataTypes.DATE, - lastScanVersion: DataTypes.STRING, - libraryFiles: DataTypes.JSON, - extraData: DataTypes.JSON - }, { - sequelize, - modelName: 'libraryItem', - indexes: [ - { - fields: ['createdAt'] - }, - { - fields: ['mediaId'] - }, - { - fields: ['libraryId', 'mediaType'] - }, - { - fields: ['libraryId', 'mediaId', 'mediaType'] - }, - { - fields: ['birthtime'] - }, - { - fields: ['mtime'] - } - ] - }) + { + sequelize, + modelName: 'libraryItem', + indexes: [ + { + fields: ['createdAt'] + }, + { + fields: ['mediaId'] + }, + { + fields: ['libraryId', 'mediaType'] + }, + { + fields: ['libraryId', 'mediaId', 'mediaType'] + }, + { + fields: ['birthtime'] + }, + { + fields: ['mtime'] + } + ] + } + ) const { library, libraryFolder, book, podcast } = sequelize.models library.hasMany(LibraryItem) @@ -1050,7 +1055,7 @@ class LibraryItem extends Model { }) LibraryItem.belongsTo(podcast, { foreignKey: 'mediaId', constraints: false }) - LibraryItem.addHook('afterFind', findResult => { + LibraryItem.addHook('afterFind', (findResult) => { if (!findResult) return if (!Array.isArray(findResult)) findResult = [findResult] @@ -1070,7 +1075,7 @@ class LibraryItem extends Model { } }) - LibraryItem.addHook('afterDestroy', async instance => { + LibraryItem.addHook('afterDestroy', async (instance) => { if (!instance) return const media = await instance.getMedia() if (media) { diff --git a/server/models/MediaProgress.js b/server/models/MediaProgress.js index 6214d649..5c571c73 100644 --- a/server/models/MediaProgress.js +++ b/server/models/MediaProgress.js @@ -100,38 +100,41 @@ class MediaProgress extends Model { /** * Initialize model - * + * * Polymorphic association: Book has many MediaProgress. PodcastEpisode has many MediaProgress. * @see https://sequelize.org/docs/v6/advanced-association-concepts/polymorphic-associations/ - * - * @param {import('../Database').sequelize} sequelize + * + * @param {import('../Database').sequelize} sequelize */ static init(sequelize) { - super.init({ - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true + super.init( + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + mediaItemId: DataTypes.UUIDV4, + mediaItemType: DataTypes.STRING, + duration: DataTypes.FLOAT, + currentTime: DataTypes.FLOAT, + isFinished: DataTypes.BOOLEAN, + hideFromContinueListening: DataTypes.BOOLEAN, + ebookLocation: DataTypes.STRING, + ebookProgress: DataTypes.FLOAT, + finishedAt: DataTypes.DATE, + extraData: DataTypes.JSON }, - mediaItemId: DataTypes.UUIDV4, - mediaItemType: DataTypes.STRING, - duration: DataTypes.FLOAT, - currentTime: DataTypes.FLOAT, - isFinished: DataTypes.BOOLEAN, - hideFromContinueListening: DataTypes.BOOLEAN, - ebookLocation: DataTypes.STRING, - ebookProgress: DataTypes.FLOAT, - finishedAt: DataTypes.DATE, - extraData: DataTypes.JSON - }, { - sequelize, - modelName: 'mediaProgress', - indexes: [ - { - fields: ['updatedAt'] - } - ] - }) + { + sequelize, + modelName: 'mediaProgress', + indexes: [ + { + fields: ['updatedAt'] + } + ] + } + ) const { book, podcastEpisode, user } = sequelize.models @@ -153,7 +156,7 @@ class MediaProgress extends Model { }) MediaProgress.belongsTo(podcastEpisode, { foreignKey: 'mediaItemId', constraints: false }) - MediaProgress.addHook('afterFind', findResult => { + MediaProgress.addHook('afterFind', (findResult) => { if (!findResult) return if (!Array.isArray(findResult)) findResult = [findResult] @@ -181,4 +184,4 @@ class MediaProgress extends Model { } } -module.exports = MediaProgress \ No newline at end of file +module.exports = MediaProgress diff --git a/server/models/PlaybackSession.js b/server/models/PlaybackSession.js index cca73cc5..5442387f 100644 --- a/server/models/PlaybackSession.js +++ b/server/models/PlaybackSession.js @@ -2,7 +2,6 @@ const { DataTypes, Model } = require('sequelize') const oldPlaybackSession = require('../objects/PlaybackSession') - class PlaybackSession extends Model { constructor(values, options) { super(values, options) @@ -62,7 +61,7 @@ class PlaybackSession extends Model { } ] }) - return playbackSessions.map(session => this.getOldPlaybackSession(session)) + return playbackSessions.map((session) => this.getOldPlaybackSession(session)) } static async getById(sessionId) { @@ -170,35 +169,38 @@ class PlaybackSession extends Model { /** * Initialize model - * @param {import('../Database').sequelize} sequelize + * @param {import('../Database').sequelize} sequelize */ static init(sequelize) { - super.init({ - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true + super.init( + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + mediaItemId: DataTypes.UUIDV4, + mediaItemType: DataTypes.STRING, + displayTitle: DataTypes.STRING, + displayAuthor: DataTypes.STRING, + duration: DataTypes.FLOAT, + playMethod: DataTypes.INTEGER, + mediaPlayer: DataTypes.STRING, + startTime: DataTypes.FLOAT, + currentTime: DataTypes.FLOAT, + serverVersion: DataTypes.STRING, + coverPath: DataTypes.STRING, + timeListening: DataTypes.INTEGER, + mediaMetadata: DataTypes.JSON, + date: DataTypes.STRING, + dayOfWeek: DataTypes.STRING, + extraData: DataTypes.JSON }, - mediaItemId: DataTypes.UUIDV4, - mediaItemType: DataTypes.STRING, - displayTitle: DataTypes.STRING, - displayAuthor: DataTypes.STRING, - duration: DataTypes.FLOAT, - playMethod: DataTypes.INTEGER, - mediaPlayer: DataTypes.STRING, - startTime: DataTypes.FLOAT, - currentTime: DataTypes.FLOAT, - serverVersion: DataTypes.STRING, - coverPath: DataTypes.STRING, - timeListening: DataTypes.INTEGER, - mediaMetadata: DataTypes.JSON, - date: DataTypes.STRING, - dayOfWeek: DataTypes.STRING, - extraData: DataTypes.JSON - }, { - sequelize, - modelName: 'playbackSession' - }) + { + sequelize, + modelName: 'playbackSession' + } + ) const { book, podcastEpisode, user, device, library } = sequelize.models @@ -229,7 +231,7 @@ class PlaybackSession extends Model { }) PlaybackSession.belongsTo(podcastEpisode, { foreignKey: 'mediaItemId', constraints: false }) - PlaybackSession.addHook('afterFind', findResult => { + PlaybackSession.addHook('afterFind', (findResult) => { if (!findResult) return if (!Array.isArray(findResult)) findResult = [findResult] diff --git a/server/models/Playlist.js b/server/models/Playlist.js index fedc83b2..fbc5f96a 100644 --- a/server/models/Playlist.js +++ b/server/models/Playlist.js @@ -23,29 +23,6 @@ class Playlist extends Model { this.updatedAt } - static async getOldPlaylists() { - const playlists = await this.findAll({ - include: { - model: this.sequelize.models.playlistMediaItem, - include: [ - { - model: this.sequelize.models.book, - include: this.sequelize.models.libraryItem - }, - { - model: this.sequelize.models.podcastEpisode, - include: { - model: this.sequelize.models.podcast, - include: this.sequelize.models.libraryItem - } - } - ] - }, - order: [['playlistMediaItems', 'order', 'ASC']] - }) - return playlists.map((p) => this.getOldPlaylist(p)) - } - static getOldPlaylist(playlistExpanded) { const items = playlistExpanded.playlistMediaItems .map((pmi) => { @@ -76,8 +53,8 @@ class Playlist extends Model { /** * Get old playlist toJSONExpanded - * @param {[string[]]} include - * @returns {Promise} oldPlaylist.toJSONExpanded + * @param {string[]} [include] + * @returns {Promise} oldPlaylist.toJSONExpanded */ async getOldJsonExpanded(include) { this.playlistMediaItems = diff --git a/server/models/PlaylistMediaItem.js b/server/models/PlaylistMediaItem.js index 8decc7ed..25e7b8c5 100644 --- a/server/models/PlaylistMediaItem.js +++ b/server/models/PlaylistMediaItem.js @@ -35,24 +35,27 @@ class PlaylistMediaItem extends Model { /** * Initialize model - * @param {import('../Database').sequelize} sequelize + * @param {import('../Database').sequelize} sequelize */ static init(sequelize) { - super.init({ - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true + super.init( + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + mediaItemId: DataTypes.UUIDV4, + mediaItemType: DataTypes.STRING, + order: DataTypes.INTEGER }, - mediaItemId: DataTypes.UUIDV4, - mediaItemType: DataTypes.STRING, - order: DataTypes.INTEGER - }, { - sequelize, - timestamps: true, - updatedAt: false, - modelName: 'playlistMediaItem' - }) + { + sequelize, + timestamps: true, + updatedAt: false, + modelName: 'playlistMediaItem' + } + ) const { book, podcastEpisode, playlist } = sequelize.models @@ -74,7 +77,7 @@ class PlaylistMediaItem extends Model { }) PlaylistMediaItem.belongsTo(podcastEpisode, { foreignKey: 'mediaItemId', constraints: false }) - PlaylistMediaItem.addHook('afterFind', findResult => { + PlaylistMediaItem.addHook('afterFind', (findResult) => { if (!findResult) return if (!Array.isArray(findResult)) findResult = [findResult] diff --git a/server/models/Podcast.js b/server/models/Podcast.js index 940ae0ab..60f879d0 100644 --- a/server/models/Podcast.js +++ b/server/models/Podcast.js @@ -3,7 +3,7 @@ const { DataTypes, Model } = require('sequelize') /** * @typedef PodcastExpandedProperties * @property {import('./PodcastEpisode')[]} podcastEpisodes - * + * * @typedef {Podcast & PodcastExpandedProperties} PodcastExpanded */ @@ -61,7 +61,7 @@ class Podcast extends Model { static getOldPodcast(libraryItemExpanded) { const podcastExpanded = libraryItemExpanded.media - const podcastEpisodes = podcastExpanded.podcastEpisodes?.map(ep => ep.getOldPodcastEpisode(libraryItemExpanded.id).toJSON()).sort((a, b) => a.index - b.index) + const podcastEpisodes = podcastExpanded.podcastEpisodes?.map((ep) => ep.getOldPodcastEpisode(libraryItemExpanded.id).toJSON()).sort((a, b) => a.index - b.index) return { id: podcastExpanded.id, libraryItemId: libraryItemExpanded.id, @@ -140,42 +140,45 @@ class Podcast extends Model { /** * Initialize model - * @param {import('../Database').sequelize} sequelize + * @param {import('../Database').sequelize} sequelize */ static init(sequelize) { - super.init({ - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true - }, - title: DataTypes.STRING, - titleIgnorePrefix: DataTypes.STRING, - author: DataTypes.STRING, - releaseDate: DataTypes.STRING, - feedURL: DataTypes.STRING, - imageURL: DataTypes.STRING, - description: DataTypes.TEXT, - itunesPageURL: DataTypes.STRING, - itunesId: DataTypes.STRING, - itunesArtistId: DataTypes.STRING, - language: DataTypes.STRING, - podcastType: DataTypes.STRING, - explicit: DataTypes.BOOLEAN, + super.init( + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + title: DataTypes.STRING, + titleIgnorePrefix: DataTypes.STRING, + author: DataTypes.STRING, + releaseDate: DataTypes.STRING, + feedURL: DataTypes.STRING, + imageURL: DataTypes.STRING, + description: DataTypes.TEXT, + itunesPageURL: DataTypes.STRING, + itunesId: DataTypes.STRING, + itunesArtistId: DataTypes.STRING, + language: DataTypes.STRING, + podcastType: DataTypes.STRING, + explicit: DataTypes.BOOLEAN, - autoDownloadEpisodes: DataTypes.BOOLEAN, - autoDownloadSchedule: DataTypes.STRING, - lastEpisodeCheck: DataTypes.DATE, - maxEpisodesToKeep: DataTypes.INTEGER, - maxNewEpisodesToDownload: DataTypes.INTEGER, - coverPath: DataTypes.STRING, - tags: DataTypes.JSON, - genres: DataTypes.JSON - }, { - sequelize, - modelName: 'podcast' - }) + autoDownloadEpisodes: DataTypes.BOOLEAN, + autoDownloadSchedule: DataTypes.STRING, + lastEpisodeCheck: DataTypes.DATE, + maxEpisodesToKeep: DataTypes.INTEGER, + maxNewEpisodesToDownload: DataTypes.INTEGER, + coverPath: DataTypes.STRING, + tags: DataTypes.JSON, + genres: DataTypes.JSON + }, + { + sequelize, + modelName: 'podcast' + } + ) } } -module.exports = Podcast \ No newline at end of file +module.exports = Podcast diff --git a/server/models/PodcastEpisode.js b/server/models/PodcastEpisode.js index 2fdefb86..1707fbd5 100644 --- a/server/models/PodcastEpisode.js +++ b/server/models/PodcastEpisode.js @@ -54,7 +54,7 @@ class PodcastEpisode extends Model { } /** - * @param {string} libraryItemId + * @param {string} libraryItemId * @returns {oldPodcastEpisode} */ getOldPodcastEpisode(libraryItemId = null) { @@ -125,40 +125,43 @@ class PodcastEpisode extends Model { /** * Initialize model - * @param {import('../Database').sequelize} sequelize + * @param {import('../Database').sequelize} sequelize */ static init(sequelize) { - super.init({ - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true - }, - index: DataTypes.INTEGER, - season: DataTypes.STRING, - episode: DataTypes.STRING, - episodeType: DataTypes.STRING, - title: DataTypes.STRING, - subtitle: DataTypes.STRING(1000), - description: DataTypes.TEXT, - pubDate: DataTypes.STRING, - enclosureURL: DataTypes.STRING, - enclosureSize: DataTypes.BIGINT, - enclosureType: DataTypes.STRING, - publishedAt: DataTypes.DATE, + super.init( + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + index: DataTypes.INTEGER, + season: DataTypes.STRING, + episode: DataTypes.STRING, + episodeType: DataTypes.STRING, + title: DataTypes.STRING, + subtitle: DataTypes.STRING(1000), + description: DataTypes.TEXT, + pubDate: DataTypes.STRING, + enclosureURL: DataTypes.STRING, + enclosureSize: DataTypes.BIGINT, + enclosureType: DataTypes.STRING, + publishedAt: DataTypes.DATE, - audioFile: DataTypes.JSON, - chapters: DataTypes.JSON, - extraData: DataTypes.JSON - }, { - sequelize, - modelName: 'podcastEpisode', - indexes: [ - { - fields: ['createdAt'] - } - ] - }) + audioFile: DataTypes.JSON, + chapters: DataTypes.JSON, + extraData: DataTypes.JSON + }, + { + sequelize, + modelName: 'podcastEpisode', + indexes: [ + { + fields: ['createdAt'] + } + ] + } + ) const { podcast } = sequelize.models podcast.hasMany(PodcastEpisode, { @@ -168,4 +171,4 @@ class PodcastEpisode extends Model { } } -module.exports = PodcastEpisode \ No newline at end of file +module.exports = PodcastEpisode diff --git a/server/models/Series.js b/server/models/Series.js index 81c27a8b..9f8f1c56 100644 --- a/server/models/Series.js +++ b/server/models/Series.js @@ -24,7 +24,7 @@ class Series extends Model { static async getAllOldSeries() { const series = await this.findAll() - return series.map(se => se.getOldSeries()) + return series.map((se) => se.getOldSeries()) } getOldSeries() { @@ -77,7 +77,7 @@ class Series extends Model { /** * Get oldSeries by id - * @param {string} seriesId + * @param {string} seriesId * @returns {Promise} */ static async getOldById(seriesId) { @@ -88,7 +88,7 @@ class Series extends Model { /** * Check if series exists - * @param {string} seriesId + * @param {string} seriesId * @returns {Promise} */ static async checkExistsById(seriesId) { @@ -97,58 +97,65 @@ class Series extends Model { /** * Get old series by name and libraryId. name case insensitive - * - * @param {string} seriesName - * @param {string} libraryId + * + * @param {string} seriesName + * @param {string} libraryId * @returns {Promise} */ static async getOldByNameAndLibrary(seriesName, libraryId) { - const series = (await this.findOne({ - where: [ - where(fn('lower', col('name')), seriesName.toLowerCase()), - { - libraryId - } - ] - }))?.getOldSeries() + const series = ( + await this.findOne({ + where: [ + where(fn('lower', col('name')), seriesName.toLowerCase()), + { + libraryId + } + ] + }) + )?.getOldSeries() return series } /** * Initialize model - * @param {import('../Database').sequelize} sequelize + * @param {import('../Database').sequelize} sequelize */ static init(sequelize) { - super.init({ - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true - }, - name: DataTypes.STRING, - nameIgnorePrefix: DataTypes.STRING, - description: DataTypes.TEXT - }, { - sequelize, - modelName: 'series', - indexes: [ - { - fields: [{ - name: 'name', - collate: 'NOCASE' - }] + super.init( + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true }, - // { - // fields: [{ - // name: 'nameIgnorePrefix', - // collate: 'NOCASE' - // }] - // }, - { - fields: ['libraryId'] - } - ] - }) + name: DataTypes.STRING, + nameIgnorePrefix: DataTypes.STRING, + description: DataTypes.TEXT + }, + { + sequelize, + modelName: 'series', + indexes: [ + { + fields: [ + { + name: 'name', + collate: 'NOCASE' + } + ] + }, + // { + // fields: [{ + // name: 'nameIgnorePrefix', + // collate: 'NOCASE' + // }] + // }, + { + fields: ['libraryId'] + } + ] + } + ) const { library } = sequelize.models library.hasMany(Series, { @@ -158,4 +165,4 @@ class Series extends Model { } } -module.exports = Series \ No newline at end of file +module.exports = Series diff --git a/server/models/Setting.js b/server/models/Setting.js index c3348e24..1fffa32c 100644 --- a/server/models/Setting.js +++ b/server/models/Setting.js @@ -19,12 +19,11 @@ class Setting extends Model { } static async getOldSettings() { - const settings = (await this.findAll()).map(se => se.value) + const settings = (await this.findAll()).map((se) => se.value) - - const emailSettingsJson = settings.find(se => se.id === 'email-settings') - const serverSettingsJson = settings.find(se => se.id === 'server-settings') - const notificationSettingsJson = settings.find(se => se.id === 'notification-settings') + const emailSettingsJson = settings.find((se) => se.id === 'email-settings') + const serverSettingsJson = settings.find((se) => se.id === 'server-settings') + const notificationSettingsJson = settings.find((se) => se.id === 'notification-settings') return { settings, @@ -43,20 +42,23 @@ class Setting extends Model { /** * Initialize model - * @param {import('../Database').sequelize} sequelize + * @param {import('../Database').sequelize} sequelize */ static init(sequelize) { - super.init({ - key: { - type: DataTypes.STRING, - primaryKey: true + super.init( + { + key: { + type: DataTypes.STRING, + primaryKey: true + }, + value: DataTypes.JSON }, - value: DataTypes.JSON - }, { - sequelize, - modelName: 'setting' - }) + { + sequelize, + modelName: 'setting' + } + ) } } -module.exports = Setting \ No newline at end of file +module.exports = Setting diff --git a/server/models/User.js b/server/models/User.js index 220c0c40..a714ca0f 100644 --- a/server/models/User.js +++ b/server/models/User.js @@ -1,4 +1,4 @@ -const uuidv4 = require("uuid").v4 +const uuidv4 = require('uuid').v4 const sequelize = require('sequelize') const Logger = require('../Logger') const oldUser = require('../objects/user/User') @@ -45,17 +45,17 @@ class User extends Model { const users = await this.findAll({ include: this.sequelize.models.mediaProgress }) - return users.map(u => this.getOldUser(u)) + return users.map((u) => this.getOldUser(u)) } /** * Get old user model from new - * - * @param {Object} userExpanded + * + * @param {Object} userExpanded * @returns {oldUser} */ static getOldUser(userExpanded) { - const mediaProgress = userExpanded.mediaProgresses.map(mp => mp.getOldMediaProgress()) + const mediaProgress = userExpanded.mediaProgresses.map((mp) => mp.getOldMediaProgress()) const librariesAccessible = userExpanded.permissions?.librariesAccessible || [] const itemTagsSelected = userExpanded.permissions?.itemTagsSelected || [] @@ -86,8 +86,8 @@ class User extends Model { } /** - * - * @param {oldUser} oldUser + * + * @param {oldUser} oldUser * @returns {Promise} */ static createFromOld(oldUser) { @@ -97,8 +97,8 @@ class User extends Model { /** * Update User from old user model - * - * @param {oldUser} oldUser + * + * @param {oldUser} oldUser * @param {boolean} [hooks=true] Run before / after bulk update hooks? * @returns {Promise} */ @@ -109,16 +109,18 @@ class User extends Model { where: { id: user.id } - }).then((result) => result[0] > 0).catch((error) => { - Logger.error(`[User] Failed to save user ${oldUser.id}`, error) - return false }) + .then((result) => result[0] > 0) + .catch((error) => { + Logger.error(`[User] Failed to save user ${oldUser.id}`, error) + return false + }) } /** * Get new User model from old - * - * @param {oldUser} oldUser + * + * @param {oldUser} oldUser * @returns {Object} */ static getFromOld(oldUser) { @@ -160,9 +162,9 @@ class User extends Model { /** * Create root user - * @param {string} username - * @param {string} pash - * @param {Auth} auth + * @param {string} username + * @param {string} pash + * @param {Auth} auth * @returns {Promise} */ static async createRootUser(username, pash, auth) { @@ -185,15 +187,15 @@ class User extends Model { /** * Create user from openid userinfo - * @param {Object} userinfo - * @param {Auth} auth + * @param {Object} userinfo + * @param {Auth} auth * @returns {Promise} */ static async createUserFromOpenIdUserInfo(userinfo, auth) { const userId = uuidv4() // TODO: Ensure username is unique? const username = userinfo.preferred_username || userinfo.name || userinfo.sub - const email = (userinfo.email && userinfo.email_verified) ? userinfo.email : null + const email = userinfo.email && userinfo.email_verified ? userinfo.email : null const token = await auth.generateAccessToken({ id: userId, username }) @@ -218,7 +220,7 @@ class User extends Model { /** * Get a user by id or by the old database id * @temp User ids were updated in v2.3.0 migration and old API tokens may still use that id - * @param {string} userId + * @param {string} userId * @returns {Promise} null if not found */ static async getUserByIdOrOldId(userId) { @@ -244,7 +246,7 @@ class User extends Model { /** * Get user by username case insensitive - * @param {string} username + * @param {string} username * @returns {Promise} returns null if not found */ static async getUserByUsername(username) { @@ -263,7 +265,7 @@ class User extends Model { /** * Get user by email case insensitive - * @param {string} username + * @param {string} username * @returns {Promise} returns null if not found */ static async getUserByEmail(email) { @@ -282,7 +284,7 @@ class User extends Model { /** * Get user by id - * @param {string} userId + * @param {string} userId * @returns {Promise} returns null if not found */ static async getUserById(userId) { @@ -296,7 +298,7 @@ class User extends Model { /** * Get user by openid sub - * @param {string} sub + * @param {string} sub * @returns {Promise} returns null if not found */ static async getUserByOpenIDSub(sub) { @@ -317,7 +319,7 @@ class User extends Model { const users = await this.findAll({ attributes: ['id', 'username'] }) - return users.map(u => { + return users.map((u) => { return { id: u.id, username: u.username @@ -340,37 +342,40 @@ class User extends Model { /** * Initialize model - * @param {import('../Database').sequelize} sequelize + * @param {import('../Database').sequelize} sequelize */ static init(sequelize) { - super.init({ - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true + super.init( + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + username: DataTypes.STRING, + email: DataTypes.STRING, + pash: DataTypes.STRING, + type: DataTypes.STRING, + token: DataTypes.STRING, + isActive: { + type: DataTypes.BOOLEAN, + defaultValue: false + }, + isLocked: { + type: DataTypes.BOOLEAN, + defaultValue: false + }, + lastSeen: DataTypes.DATE, + permissions: DataTypes.JSON, + bookmarks: DataTypes.JSON, + extraData: DataTypes.JSON }, - username: DataTypes.STRING, - email: DataTypes.STRING, - pash: DataTypes.STRING, - type: DataTypes.STRING, - token: DataTypes.STRING, - isActive: { - type: DataTypes.BOOLEAN, - defaultValue: false - }, - isLocked: { - type: DataTypes.BOOLEAN, - defaultValue: false - }, - lastSeen: DataTypes.DATE, - permissions: DataTypes.JSON, - bookmarks: DataTypes.JSON, - extraData: DataTypes.JSON - }, { - sequelize, - modelName: 'user' - }) + { + sequelize, + modelName: 'user' + } + ) } } -module.exports = User \ No newline at end of file +module.exports = User