From 54ca58e6107f5d7c79546303690e5b2bde7c3e98 Mon Sep 17 00:00:00 2001 From: advplyr Date: Sun, 19 Mar 2023 15:19:22 -0500 Subject: [PATCH] Update model casing & associations --- server/Database.js | 34 +- server/Server.js | 9 +- server/controllers2/LibraryItemController.js | 192 ++++++++ server/models/AudioBookmark.js | 49 +- server/models/AudioTrack.js | 49 +- server/models/Book.js | 14 +- server/models/BookAuthor.js | 16 +- server/models/BookChapter.js | 8 +- server/models/BookGenre.js | 16 +- server/models/BookNarrator.js | 16 +- server/models/BookSeries.js | 16 +- server/models/BookTag.js | 16 +- server/models/Collection.js | 8 +- server/models/CollectionBook.js | 16 +- server/models/Device.js | 8 +- server/models/EBookFile.js | 11 +- server/models/Feed.js | 82 ++-- server/models/FeedEpisode.js | 8 +- server/models/FileMetadata.js | 6 +- server/models/Genre.js | 2 +- server/models/Library.js | 2 +- server/models/LibraryFile.js | 12 +- server/models/LibraryFolder.js | 8 +- server/models/LibraryItem.js | 55 ++- server/models/LibrarySetting.js | 8 +- server/models/MediaFile.js | 8 +- server/models/MediaProgress.js | 58 +-- server/models/MediaStream.js | 8 +- server/models/Notification.js | 2 +- server/models/Person.js | 8 +- server/models/PlaybackSession.js | 53 ++- server/models/PlaybackSessionListenTime.js | 8 +- server/models/Playlist.js | 12 +- server/models/PlaylistMediaItem.js | 49 +- server/models/Podcast.js | 11 +- server/models/PodcastEpisode.js | 8 +- server/models/PodcastGenre.js | 16 +- server/models/PodcastTag.js | 16 +- server/models/Series.js | 2 +- server/models/Setting.js | 2 +- server/models/Tag.js | 2 +- server/models/User.js | 2 +- server/models/UserPermission.js | 8 +- server/routers/ApiRouter2.js | 15 + server/utils/migrations/dbMigration.js | 442 +++++++++---------- 45 files changed, 830 insertions(+), 561 deletions(-) create mode 100644 server/controllers2/LibraryItemController.js create mode 100644 server/routers/ApiRouter2.js diff --git a/server/Database.js b/server/Database.js index f9b5f7a1..ba5ac290 100644 --- a/server/Database.js +++ b/server/Database.js @@ -12,12 +12,12 @@ class Database { return this.sequelize?.models || {} } - async init() { + async init(force = false) { if (!await this.connect()) { throw new Error('Database connection failed') } - await this.buildModels() + await this.buildModels(force) Logger.info(`[Database] Db initialized`, Object.keys(this.sequelize.models)) } @@ -30,6 +30,9 @@ class Database { logging: false }) + // Helper function + this.sequelize.uppercaseFirst = str => str ? `${str[0].toUpperCase()}${str.substr(1)}` : '' + try { await this.sequelize.authenticate() Logger.info(`[Database] Db connection was successful`) @@ -40,15 +43,15 @@ class Database { } } - buildModels() { + buildModels(force = false) { require('./models/User')(this.sequelize) require('./models/FileMetadata')(this.sequelize) - require('./models/Library')(this.sequelize) - require('./models/LibraryFolder')(this.sequelize) - require('./models/LibraryItem')(this.sequelize) require('./models/EBookFile')(this.sequelize) require('./models/Book')(this.sequelize) require('./models/Podcast')(this.sequelize) + require('./models/Library')(this.sequelize) + require('./models/LibraryFolder')(this.sequelize) + require('./models/LibraryItem')(this.sequelize) require('./models/PodcastEpisode')(this.sequelize) require('./models/MediaProgress')(this.sequelize) require('./models/LibraryFile')(this.sequelize) @@ -82,24 +85,7 @@ class Database { require('./models/Notification')(this.sequelize) require('./models/UserPermission')(this.sequelize) - return this.sequelize.sync({ force: true }) - } - - async createTestUser() { - const User = this.sequelize.models.User - - let user = await User.findOne({ - where: { - username: 'Tester' - } - }) - - if (user) { - Logger.info(`[Database] Tester user was found`, user.toJSON()) - } else { - user = await User.create({ username: 'Tester' }) - Logger.info(`[Database] Created Tester user`, user.toJSON()) - } + return this.sequelize.sync({ force }) } } diff --git a/server/Server.js b/server/Server.js index 81109b5b..136ca0b7 100644 --- a/server/Server.js +++ b/server/Server.js @@ -22,6 +22,7 @@ const Database = require('./Database') const SocketAuthority = require('./SocketAuthority') const ApiRouter = require('./routers/ApiRouter') +const ApiRouter2 = require('./routers/ApiRouter2') const HlsRouter = require('./routers/HlsRouter') const StaticRouter = require('./routers/StaticRouter') @@ -84,6 +85,7 @@ class Server { // Routers this.apiRouter = new ApiRouter(this) + this.hlsRouter = new HlsRouter(this.db, this.auth, this.playbackSessionManager) this.staticRouter = new StaticRouter(this.db) @@ -102,9 +104,9 @@ class Server { await this.playbackSessionManager.removeOrphanStreams() // TODO: Test new db connection - await Database.init() - // await Database.createTestUser() - await dbMigration3.migrate() + const force = true + await Database.init(force) + if (force) await dbMigration3.migrate() const previousVersion = await this.db.checkPreviousVersion() // Returns null if same server version if (previousVersion) { @@ -169,6 +171,7 @@ class Server { // Static folder router.use(express.static(Path.join(global.appRoot, 'static'))) + router.use('/api/v1', new ApiRouter2(this).router) router.use('/api', this.authMiddleware.bind(this), this.apiRouter.router) router.use('/hls', this.authMiddleware.bind(this), this.hlsRouter.router) router.use('/s', this.authMiddleware.bind(this), this.staticRouter.router) diff --git a/server/controllers2/LibraryItemController.js b/server/controllers2/LibraryItemController.js new file mode 100644 index 00000000..608ac1d9 --- /dev/null +++ b/server/controllers2/LibraryItemController.js @@ -0,0 +1,192 @@ +const Database = require('../Database') + +class LibraryItemController { + constructor() { } + + // Example get library item fully expanded or minified + async get(req, res) { + const key = req.query.minified == 1 ? 'minified' : 'full' + const include = { + minified: [ + { + model: Database.models.book, + include: [ + { + model: Database.models.audioTrack + }, + { + model: Database.models.genre, + through: { + attributes: [] + } + }, + { + model: Database.models.tag, + through: { + attributes: [] + } + }, + { + model: Database.models.person, + as: 'authors', + through: { + attributes: [] + } + }, + { + model: Database.models.person, + as: 'narrators', + through: { + attributes: [] + } + }, + { + model: Database.models.series, + through: { + attributes: ['sequence'] + } + }, + { + model: Database.models.bookChapter + }, + { + model: Database.models.eBookFile, + include: 'fileMetadata' + } + ] + }, + { + model: Database.models.podcast, + include: [ + { + model: Database.models.podcastEpisode, + include: { + model: Database.models.audioTrack + } + }, + { + model: Database.models.genre, + through: { + attributes: [] + } + }, + { + model: Database.models.tag, + through: { + attributes: [] + } + }, + ] + } + ], + full: [ + { + model: Database.models.book, + include: [ + { + model: Database.models.fileMetadata, + as: 'imageFile' + }, + { + model: Database.models.audioTrack, + include: { + model: Database.models.mediaFile, + include: [ + 'fileMetadata', + 'mediaStreams' + ] + } + }, + { + model: Database.models.genre, + through: { + attributes: [] + } + }, + { + model: Database.models.tag, + through: { + attributes: [] + } + }, + { + model: Database.models.person, + as: 'authors', + through: { + attributes: [] + } + }, + { + model: Database.models.person, + as: 'narrators', + through: { + attributes: [] + } + }, + { + model: Database.models.series, + through: { + attributes: ['sequence'] + } + }, + { + model: Database.models.bookChapter + }, + { + model: Database.models.eBookFile, + include: 'fileMetadata' + } + ] + }, + { + model: Database.models.podcast, + include: [ + { + model: Database.models.fileMetadata, + as: 'imageFile' + }, + { + model: Database.models.podcastEpisode, + include: { + model: Database.models.audioTrack, + include: { + model: Database.models.mediaFile, + include: [ + 'fileMetadata', + 'mediaStreams' + ] + } + } + }, + { + model: Database.models.genre, + through: { + attributes: [] + } + }, + { + model: Database.models.tag, + through: { + attributes: [] + } + }, + ] + }, + { + model: Database.models.libraryFile, + include: 'fileMetadata' + }, + { + model: Database.models.libraryFolder, + include: 'library' + } + ] + } + const LibraryItem = await Database.models.libraryItem.findByPk(req.params.id, { + include: include[key] + }) + + res.json(LibraryItem) + } +} +module.exports = new LibraryItemController() \ No newline at end of file diff --git a/server/models/AudioBookmark.js b/server/models/AudioBookmark.js index fe50ed37..89d6039f 100644 --- a/server/models/AudioBookmark.js +++ b/server/models/AudioBookmark.js @@ -8,7 +8,7 @@ module.exports = (sequelize) => { class AudioBookmark extends Model { getMediaItem(options) { if (!this.mediaItemType) return Promise.resolve(null) - const mixinMethodName = `get${this.mediaItemType}` + const mixinMethodName = `get${sequelize.uppercaseFirst(this.mediaItemType)}` return this[mixinMethodName](options) } } @@ -19,52 +19,57 @@ module.exports = (sequelize) => { defaultValue: DataTypes.UUIDV4, primaryKey: true }, - MediaItemId: DataTypes.UUIDV4, + mediaItemId: DataTypes.UUIDV4, mediaItemType: DataTypes.STRING, title: DataTypes.STRING, time: DataTypes.INTEGER }, { sequelize, - modelName: 'AudioBookmark' + modelName: 'audioBookmark' }) - const { User, Book, PodcastEpisode } = sequelize.models - Book.hasMany(AudioBookmark, { - foreignKey: 'MediaItemId', + const { user, book, podcastEpisode } = sequelize.models + book.hasMany(AudioBookmark, { + foreignKey: 'mediaItemId', constraints: false, scope: { - mediaItemType: 'Book' + mediaItemType: 'book' } }) - AudioBookmark.belongsTo(Book, { foreignKey: 'MediaItemId', constraints: false }) + AudioBookmark.belongsTo(book, { foreignKey: 'mediaItemId', constraints: false }) - PodcastEpisode.hasMany(AudioBookmark, { - foreignKey: 'MediaItemId', + podcastEpisode.hasMany(AudioBookmark, { + foreignKey: 'mediaItemId', constraints: false, scope: { - mediaItemType: 'PodcastEpisode' + mediaItemType: 'podcastEpisode' } }) - AudioBookmark.belongsTo(PodcastEpisode, { foreignKey: 'MediaItemId', constraints: false }) + AudioBookmark.belongsTo(podcastEpisode, { foreignKey: 'mediaItemId', constraints: false }) AudioBookmark.addHook('afterFind', findResult => { + if (!findResult) return + if (!Array.isArray(findResult)) findResult = [findResult] + for (const instance of findResult) { - if (instance.mediaItemType === 'Book' && instance.Book !== undefined) { - instance.MediaItem = instance.Book - } else if (instance.mediaItemType === 'PodcastEpisode' && instance.PodcastEpisode !== undefined) { - instance.MediaItem = instance.PodcastEpisode + if (instance.mediaItemType === 'book' && instance.book !== undefined) { + instance.mediaItem = instance.book + instance.dataValues.mediaItem = instance.dataValues.book + } else if (instance.mediaItemType === 'podcastEpisode' && instance.podcastEpisode !== undefined) { + instance.mediaItem = instance.podcastEpisode + instance.dataValues.mediaItem = instance.dataValues.podcastEpisode } // To prevent mistakes: - delete instance.Book - delete instance.dataValues.Book - delete instance.PodcastEpisode - delete instance.dataValues.PodcastEpisode + delete instance.book + delete instance.dataValues.book + delete instance.podcastEpisode + delete instance.dataValues.podcastEpisode } }) - User.hasMany(AudioBookmark) - AudioBookmark.belongsTo(User) + user.hasMany(AudioBookmark) + AudioBookmark.belongsTo(user) return AudioBookmark } \ No newline at end of file diff --git a/server/models/AudioTrack.js b/server/models/AudioTrack.js index 8a4b9f65..3b119d3b 100644 --- a/server/models/AudioTrack.js +++ b/server/models/AudioTrack.js @@ -8,7 +8,7 @@ module.exports = (sequelize) => { class AudioTrack extends Model { getMediaItem(options) { if (!this.mediaItemType) return Promise.resolve(null) - const mixinMethodName = `get${this.mediaItemType}` + const mixinMethodName = `get${sequelize.uppercaseFirst(this.mediaItemType)}` return this[mixinMethodName](options) } } @@ -19,7 +19,7 @@ module.exports = (sequelize) => { defaultValue: DataTypes.UUIDV4, primaryKey: true }, - MediaItemId: DataTypes.UUIDV4, + mediaItemId: DataTypes.UUIDV4, mediaItemType: DataTypes.STRING, index: DataTypes.INTEGER, startOffset: DataTypes.FLOAT, @@ -31,45 +31,50 @@ module.exports = (sequelize) => { discNumber: DataTypes.INTEGER }, { sequelize, - modelName: 'AudioTrack' + modelName: 'audioTrack' }) - const { Book, PodcastEpisode, MediaFile } = sequelize.models + const { book, podcastEpisode, mediaFile } = sequelize.models - MediaFile.hasOne(AudioTrack) - AudioTrack.belongsTo(MediaFile) + mediaFile.hasOne(AudioTrack) + AudioTrack.belongsTo(mediaFile) - Book.hasMany(AudioTrack, { - foreignKey: 'MediaItemId', + book.hasMany(AudioTrack, { + foreignKey: 'mediaItemId', constraints: false, scope: { - mediaItemType: 'Book' + mediaItemType: 'book' } }) - AudioTrack.belongsTo(Book, { foreignKey: 'MediaItemId', constraints: false }) + AudioTrack.belongsTo(book, { foreignKey: 'mediaItemId', constraints: false }) - PodcastEpisode.hasOne(AudioTrack, { - foreignKey: 'MediaItemId', + podcastEpisode.hasOne(AudioTrack, { + foreignKey: 'mediaItemId', constraints: false, scope: { - mediaItemType: 'PodcastEpisode' + mediaItemType: 'podcastEpisode' } }) - AudioTrack.belongsTo(PodcastEpisode, { foreignKey: 'MediaItemId', constraints: false }) + AudioTrack.belongsTo(podcastEpisode, { foreignKey: 'mediaItemId', constraints: false }) AudioTrack.addHook('afterFind', findResult => { + if (!findResult) return + if (!Array.isArray(findResult)) findResult = [findResult] + for (const instance of findResult) { - if (instance.mediaItemType === 'Book' && instance.Book !== undefined) { - instance.MediaItem = instance.Book - } else if (instance.mediaItemType === 'PodcastEpisode' && instance.PodcastEpisode !== undefined) { - instance.MediaItem = instance.PodcastEpisode + if (instance.mediaItemType === 'book' && instance.book !== undefined) { + instance.mediaItem = instance.book + instance.dataValues.mediaItem = instance.dataValues.book + } else if (instance.mediaItemType === 'podcastEpisode' && instance.podcastEpisode !== undefined) { + instance.mediaItem = instance.podcastEpisode + instance.dataValues.mediaItem = instance.dataValues.podcastEpisode } // To prevent mistakes: - delete instance.Book - delete instance.dataValues.Book - delete instance.PodcastEpisode - delete instance.dataValues.PodcastEpisode + delete instance.book + delete instance.dataValues.book + delete instance.podcastEpisode + delete instance.dataValues.podcastEpisode } }) diff --git a/server/models/Book.js b/server/models/Book.js index b677c69a..21bbee59 100644 --- a/server/models/Book.js +++ b/server/models/Book.js @@ -23,18 +23,16 @@ module.exports = (sequelize) => { lastCoverSearch: DataTypes.DATE }, { sequelize, - modelName: 'Book' + modelName: 'book' }) - const { LibraryItem, FileMetadata, EBookFile } = sequelize.models - LibraryItem.hasOne(Book) - Book.belongsTo(LibraryItem) + const { fileMetadata, eBookFile } = sequelize.models - FileMetadata.hasOne(Book, { foreignKey: 'ImageFileId ' }) - Book.belongsTo(FileMetadata, { as: 'ImageFile', foreignKey: 'ImageFileId' }) // Ref: https://sequelize.org/docs/v6/core-concepts/assocs/#defining-an-alias + fileMetadata.hasOne(Book, { foreignKey: 'imageFileId' }) + Book.belongsTo(fileMetadata, { as: 'imageFile', foreignKey: 'imageFileId' }) // Ref: https://sequelize.org/docs/v6/core-concepts/assocs/#defining-an-alias - EBookFile.hasOne(Book) - Book.belongsTo(EBookFile) + eBookFile.hasOne(Book) + Book.belongsTo(eBookFile) return Book } \ No newline at end of file diff --git a/server/models/BookAuthor.js b/server/models/BookAuthor.js index 686c8e89..11051cbf 100644 --- a/server/models/BookAuthor.js +++ b/server/models/BookAuthor.js @@ -11,21 +11,21 @@ module.exports = (sequelize) => { } }, { sequelize, - modelName: 'BookAuthor', + modelName: 'bookAuthor', timestamps: 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 - const { Book, Person } = sequelize.models - Book.belongsToMany(Person, { through: BookAuthor }) - Person.belongsToMany(Book, { through: BookAuthor }) + const { book, person } = sequelize.models + book.belongsToMany(person, { through: BookAuthor, as: 'authors', otherKey: 'authorId' }) + person.belongsToMany(book, { through: BookAuthor, foreignKey: 'authorId' }) - Book.hasMany(BookAuthor) - BookAuthor.belongsTo(Book) + book.hasMany(BookAuthor) + BookAuthor.belongsTo(book) - Person.hasMany(BookAuthor) - BookAuthor.belongsTo(Person) + person.hasMany(BookAuthor, { foreignKey: 'authorId' }) + BookAuthor.belongsTo(person, { as: 'author', foreignKey: 'authorId' }) return BookAuthor } \ No newline at end of file diff --git a/server/models/BookChapter.js b/server/models/BookChapter.js index 1d9af4aa..5aca0606 100644 --- a/server/models/BookChapter.js +++ b/server/models/BookChapter.js @@ -15,13 +15,13 @@ module.exports = (sequelize) => { end: DataTypes.FLOAT }, { sequelize, - modelName: 'BookChapter' + modelName: 'bookChapter' }) - const { Book } = sequelize.models + const { book } = sequelize.models - Book.hasMany(BookChapter) - BookChapter.belongsTo(Book) + book.hasMany(BookChapter) + BookChapter.belongsTo(book) return BookChapter } \ No newline at end of file diff --git a/server/models/BookGenre.js b/server/models/BookGenre.js index 0fde5353..6a41793a 100644 --- a/server/models/BookGenre.js +++ b/server/models/BookGenre.js @@ -11,21 +11,21 @@ module.exports = (sequelize) => { } }, { sequelize, - modelName: 'BookGenre', + modelName: 'bookGenre', timestamps: 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 - const { Book, Genre } = sequelize.models - Book.belongsToMany(Genre, { through: BookGenre }) - Genre.belongsToMany(Book, { through: BookGenre }) + const { book, genre } = sequelize.models + book.belongsToMany(genre, { through: BookGenre }) + genre.belongsToMany(book, { through: BookGenre }) - Book.hasMany(BookGenre) - BookGenre.belongsTo(Book) + book.hasMany(BookGenre) + BookGenre.belongsTo(book) - Genre.hasMany(BookGenre) - BookGenre.belongsTo(Genre) + genre.hasMany(BookGenre) + BookGenre.belongsTo(genre) return BookGenre } \ No newline at end of file diff --git a/server/models/BookNarrator.js b/server/models/BookNarrator.js index d246160e..6038553d 100644 --- a/server/models/BookNarrator.js +++ b/server/models/BookNarrator.js @@ -11,21 +11,21 @@ module.exports = (sequelize) => { } }, { sequelize, - modelName: 'BookNarrator', + modelName: 'bookNarrator', timestamps: 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 - const { Book, Person } = sequelize.models - Book.belongsToMany(Person, { through: BookNarrator }) - Person.belongsToMany(Book, { through: BookNarrator }) + const { book, person } = sequelize.models + book.belongsToMany(person, { through: BookNarrator, as: 'narrators', otherKey: 'narratorId' }) + person.belongsToMany(book, { through: BookNarrator, foreignKey: 'narratorId' }) - Book.hasMany(BookNarrator) - BookNarrator.belongsTo(Book) + book.hasMany(BookNarrator) + BookNarrator.belongsTo(book) - Person.hasMany(BookNarrator) - BookNarrator.belongsTo(Person) + person.hasMany(BookNarrator, { foreignKey: 'narratorId' }) + BookNarrator.belongsTo(person, { as: 'narrator', foreignKey: 'narratorId' }) return BookNarrator } \ No newline at end of file diff --git a/server/models/BookSeries.js b/server/models/BookSeries.js index f4cf31a3..5259f835 100644 --- a/server/models/BookSeries.js +++ b/server/models/BookSeries.js @@ -12,21 +12,21 @@ module.exports = (sequelize) => { sequence: DataTypes.STRING }, { sequelize, - modelName: 'BookSeries', + modelName: 'bookSeries', timestamps: 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 - const { Book, Series } = sequelize.models - Book.belongsToMany(Series, { through: BookSeries }) - Series.belongsToMany(Book, { through: BookSeries }) + const { book, series } = sequelize.models + book.belongsToMany(series, { through: BookSeries }) + series.belongsToMany(book, { through: BookSeries }) - Book.hasMany(BookSeries) - BookSeries.belongsTo(Book) + book.hasMany(BookSeries) + BookSeries.belongsTo(book) - Series.hasMany(BookSeries) - BookSeries.belongsTo(Series) + series.hasMany(BookSeries) + BookSeries.belongsTo(series) return BookSeries } \ No newline at end of file diff --git a/server/models/BookTag.js b/server/models/BookTag.js index 2089f623..63e36503 100644 --- a/server/models/BookTag.js +++ b/server/models/BookTag.js @@ -11,21 +11,21 @@ module.exports = (sequelize) => { } }, { sequelize, - modelName: 'BookTag', + modelName: 'bookTag', timestamps: 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 - const { Book, Tag } = sequelize.models - Book.belongsToMany(Tag, { through: BookTag }) - Tag.belongsToMany(Book, { through: BookTag }) + const { book, tag } = sequelize.models + book.belongsToMany(tag, { through: BookTag }) + tag.belongsToMany(book, { through: BookTag }) - Book.hasMany(BookTag) - BookTag.belongsTo(Book) + book.hasMany(BookTag) + BookTag.belongsTo(book) - Tag.hasMany(BookTag) - BookTag.belongsTo(Tag) + tag.hasMany(BookTag) + BookTag.belongsTo(tag) return BookTag } \ No newline at end of file diff --git a/server/models/Collection.js b/server/models/Collection.js index 42a41ac6..683c2081 100644 --- a/server/models/Collection.js +++ b/server/models/Collection.js @@ -13,13 +13,13 @@ module.exports = (sequelize) => { description: DataTypes.TEXT }, { sequelize, - modelName: 'Collection' + modelName: 'collection' }) - const { Library } = sequelize.models + const { library } = sequelize.models - Library.hasMany(Collection) - Collection.belongsTo(Library) + library.hasMany(Collection) + Collection.belongsTo(library) return Collection } \ No newline at end of file diff --git a/server/models/CollectionBook.js b/server/models/CollectionBook.js index c4032ced..022bde12 100644 --- a/server/models/CollectionBook.js +++ b/server/models/CollectionBook.js @@ -13,20 +13,20 @@ module.exports = (sequelize) => { sequelize, timestamps: true, updatedAt: false, - modelName: 'CollectionBook' + 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 - const { Book, Collection } = sequelize.models - Book.belongsToMany(Collection, { through: CollectionBook }) - Collection.belongsToMany(Book, { through: CollectionBook }) + const { book, collection } = sequelize.models + book.belongsToMany(collection, { through: CollectionBook }) + collection.belongsToMany(book, { through: CollectionBook }) - Book.hasMany(CollectionBook) - CollectionBook.belongsTo(Book) + book.hasMany(CollectionBook) + CollectionBook.belongsTo(book) - Collection.hasMany(CollectionBook) - CollectionBook.belongsTo(Collection) + collection.hasMany(CollectionBook) + CollectionBook.belongsTo(collection) return CollectionBook } \ No newline at end of file diff --git a/server/models/Device.js b/server/models/Device.js index 4f5a180d..bf379023 100644 --- a/server/models/Device.js +++ b/server/models/Device.js @@ -17,13 +17,13 @@ module.exports = (sequelize) => { deviceVersion: DataTypes.STRING // e.g. Browser version or Android SDK }, { sequelize, - modelName: 'Device' + modelName: 'device' }) - const { User } = sequelize.models + const { user } = sequelize.models - User.hasMany(Device) - Device.belongsTo(User) + user.hasMany(Device) + Device.belongsTo(user) return Device } \ No newline at end of file diff --git a/server/models/EBookFile.js b/server/models/EBookFile.js index 05edf2e7..61c485c0 100644 --- a/server/models/EBookFile.js +++ b/server/models/EBookFile.js @@ -8,16 +8,17 @@ module.exports = (sequelize) => { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4, primaryKey: true - } + }, + format: DataTypes.STRING }, { sequelize, - modelName: 'EBookFile' + modelName: 'eBookFile' }) - const { FileMetadata } = sequelize.models + const { fileMetadata } = sequelize.models - FileMetadata.hasOne(EBookFile, { foreignKey: 'FileMetadataId' }) - EBookFile.belongsTo(FileMetadata, { as: 'FileMetadata', foreignKey: 'FileMetadataId' }) + fileMetadata.hasOne(EBookFile, { foreignKey: 'fileMetadataId' }) + EBookFile.belongsTo(fileMetadata, { as: 'fileMetadata', foreignKey: 'fileMetadataId' }) return EBookFile } \ No newline at end of file diff --git a/server/models/Feed.js b/server/models/Feed.js index 8849a292..f843e7e3 100644 --- a/server/models/Feed.js +++ b/server/models/Feed.js @@ -8,7 +8,7 @@ module.exports = (sequelize) => { class Feed extends Model { getEntity(options) { if (!this.entityType) return Promise.resolve(null) - const mixinMethodName = `get${this.entityType}` + const mixinMethodName = `get${sequelize.uppercaseFirst(this.entityType)}` return this[mixinMethodName](options) } } @@ -21,7 +21,7 @@ module.exports = (sequelize) => { }, slug: DataTypes.STRING, entityType: DataTypes.STRING, - EntityId: DataTypes.UUIDV4, + entityId: DataTypes.UUIDV4, entityUpdatedAt: DataTypes.DATE, serverAddress: DataTypes.STRING, feedURL: DataTypes.STRING, @@ -38,72 +38,78 @@ module.exports = (sequelize) => { preventIndexing: DataTypes.BOOLEAN }, { sequelize, - modelName: 'Feed' + modelName: 'feed' }) - const { User, LibraryItem, Collection, Series, Playlist } = sequelize.models + const { user, libraryItem, collection, series, playlist } = sequelize.models - User.hasMany(Feed) - Feed.belongsTo(User) + user.hasMany(Feed) + Feed.belongsTo(user) - LibraryItem.hasMany(Feed, { - foreignKey: 'EntityId', + libraryItem.hasMany(Feed, { + foreignKey: 'entityId', constraints: false, scope: { - entityType: 'LibraryItem' + entityType: 'libraryItem' } }) - Feed.belongsTo(LibraryItem, { foreignKey: 'EntityId', constraints: false }) + Feed.belongsTo(libraryItem, { foreignKey: 'entityId', constraints: false }) - Collection.hasMany(Feed, { - foreignKey: 'EntityId', + collection.hasMany(Feed, { + foreignKey: 'entityId', constraints: false, scope: { - entityType: 'Collection' + entityType: 'collection' } }) - Feed.belongsTo(Collection, { foreignKey: 'EntityId', constraints: false }) + Feed.belongsTo(collection, { foreignKey: 'entityId', constraints: false }) - Series.hasMany(Feed, { - foreignKey: 'EntityId', + series.hasMany(Feed, { + foreignKey: 'entityId', constraints: false, scope: { - entityType: 'Series' + entityType: 'series' } }) - Feed.belongsTo(Series, { foreignKey: 'EntityId', constraints: false }) + Feed.belongsTo(series, { foreignKey: 'entityId', constraints: false }) - Playlist.hasMany(Feed, { - foreignKey: 'EntityId', + playlist.hasMany(Feed, { + foreignKey: 'entityId', constraints: false, scope: { - entityType: 'Playlist' + entityType: 'playlist' } }) - Feed.belongsTo(Playlist, { foreignKey: 'EntityId', constraints: false }) + Feed.belongsTo(playlist, { foreignKey: 'entityId', constraints: false }) Feed.addHook('afterFind', findResult => { + if (!findResult) return + if (!Array.isArray(findResult)) findResult = [findResult] for (const instance of findResult) { - if (instance.entityType === 'LibraryItem' && instance.LibraryItem !== undefined) { - instance.Entity = instance.LibraryItem - } else if (instance.mediaItemType === 'Collection' && instance.Collection !== undefined) { - instance.Entity = instance.Collection - } else if (instance.mediaItemType === 'Series' && instance.Series !== undefined) { - instance.Entity = instance.Series - } else if (instance.mediaItemType === 'Playlist' && instance.Playlist !== undefined) { - instance.Entity = instance.Playlist + if (instance.entityType === 'libraryItem' && instance.libraryItem !== undefined) { + instance.entity = instance.libraryItem + instance.dataValues.entity = instance.dataValues.libraryItem + } else if (instance.entityType === 'collection' && instance.collection !== undefined) { + instance.entity = instance.collection + instance.dataValues.entity = instance.dataValues.collection + } else if (instance.entityType === 'series' && instance.series !== undefined) { + instance.entity = instance.series + instance.dataValues.entity = instance.dataValues.series + } else if (instance.entityType === 'playlist' && instance.playlist !== undefined) { + instance.entity = instance.playlist + instance.dataValues.entity = instance.dataValues.playlist } // To prevent mistakes: - delete instance.LibraryItem - delete instance.dataValues.LibraryItem - delete instance.Collection - delete instance.dataValues.Collection - delete instance.Series - delete instance.dataValues.Series - delete instance.Playlist - delete instance.dataValues.Playlist + delete instance.libraryItem + delete instance.dataValues.libraryItem + delete instance.collection + delete instance.dataValues.collection + delete instance.series + delete instance.dataValues.series + delete instance.playlist + delete instance.dataValues.playlist } }) diff --git a/server/models/FeedEpisode.js b/server/models/FeedEpisode.js index 3b00d774..01081b94 100644 --- a/server/models/FeedEpisode.js +++ b/server/models/FeedEpisode.js @@ -25,13 +25,13 @@ module.exports = (sequelize) => { explicit: DataTypes.BOOLEAN }, { sequelize, - modelName: 'FeedEpisode' + modelName: 'feedEpisode' }) - const { Feed } = sequelize.models + const { feed } = sequelize.models - Feed.hasMany(FeedEpisode) - FeedEpisode.belongsTo(Feed) + feed.hasMany(FeedEpisode) + FeedEpisode.belongsTo(feed) return FeedEpisode } \ No newline at end of file diff --git a/server/models/FileMetadata.js b/server/models/FileMetadata.js index ff290eb7..9d8bb7fe 100644 --- a/server/models/FileMetadata.js +++ b/server/models/FileMetadata.js @@ -21,10 +21,10 @@ module.exports = (sequelize) => { sequelize, freezeTableName: true, // sequelize uses datum as singular of data name: { - singular: 'FileMetadata', - plural: 'FileMetadata' + singular: 'fileMetadata', + plural: 'fileMetadata' }, - modelName: 'FileMetadata' + modelName: 'fileMetadata' }) return FileMetadata diff --git a/server/models/Genre.js b/server/models/Genre.js index 5c022879..cb248b7d 100644 --- a/server/models/Genre.js +++ b/server/models/Genre.js @@ -13,7 +13,7 @@ module.exports = (sequelize) => { cleanName: DataTypes.STRING }, { sequelize, - modelName: 'Genre' + modelName: 'genre' }) return Genre diff --git a/server/models/Library.js b/server/models/Library.js index 32325fdc..6fdd3b83 100644 --- a/server/models/Library.js +++ b/server/models/Library.js @@ -18,7 +18,7 @@ module.exports = (sequelize) => { lastScanVersion: DataTypes.STRING }, { sequelize, - modelName: 'Library' + modelName: 'library' }) return Library diff --git a/server/models/LibraryFile.js b/server/models/LibraryFile.js index 54c93a4f..9905e17a 100644 --- a/server/models/LibraryFile.js +++ b/server/models/LibraryFile.js @@ -11,15 +11,15 @@ module.exports = (sequelize) => { } }, { sequelize, - modelName: 'LibraryFile' + modelName: 'libraryFile' }) - const { LibraryItem, FileMetadata } = sequelize.models - LibraryItem.hasMany(LibraryFile) - LibraryFile.belongsTo(LibraryItem) + const { libraryItem, fileMetadata } = sequelize.models + libraryItem.hasMany(LibraryFile) + LibraryFile.belongsTo(libraryItem) - FileMetadata.hasOne(LibraryFile, { foreignKey: 'FileMetadataId' }) - LibraryFile.belongsTo(FileMetadata, { as: 'FileMetadata', foreignKey: 'FileMetadataId' }) + fileMetadata.hasOne(LibraryFile, { foreignKey: 'fileMetadataId' }) + LibraryFile.belongsTo(fileMetadata, { as: 'fileMetadata', foreignKey: 'fileMetadataId' }) return LibraryFile } \ No newline at end of file diff --git a/server/models/LibraryFolder.js b/server/models/LibraryFolder.js index 028dcdf3..537947f3 100644 --- a/server/models/LibraryFolder.js +++ b/server/models/LibraryFolder.js @@ -12,12 +12,12 @@ module.exports = (sequelize) => { path: DataTypes.STRING }, { sequelize, - modelName: 'LibraryFolder' + modelName: 'libraryFolder' }) - const { Library } = sequelize.models - Library.hasMany(LibraryFolder) - LibraryFolder.belongsTo(Library) + const { library } = sequelize.models + library.hasMany(LibraryFolder) + LibraryFolder.belongsTo(library) return LibraryFolder } \ No newline at end of file diff --git a/server/models/LibraryItem.js b/server/models/LibraryItem.js index 97b8b2d0..93203aca 100644 --- a/server/models/LibraryItem.js +++ b/server/models/LibraryItem.js @@ -1,7 +1,13 @@ const { DataTypes, Model } = require('sequelize') module.exports = (sequelize) => { - class LibraryItem extends Model { } + class LibraryItem extends Model { + getMedia(options) { + if (!this.mediaType) return Promise.resolve(null) + const mixinMethodName = `get${sequelize.uppercaseFirst(this.mediaType)}` + return this[mixinMethodName](options) + } + } LibraryItem.init({ id: { @@ -12,6 +18,7 @@ module.exports = (sequelize) => { ino: DataTypes.STRING, path: DataTypes.STRING, relPath: DataTypes.STRING, + mediaId: DataTypes.UUIDV4, mediaType: DataTypes.STRING, isFile: DataTypes.BOOLEAN, isMissing: DataTypes.BOOLEAN, @@ -23,12 +30,50 @@ module.exports = (sequelize) => { lastScanVersion: DataTypes.STRING }, { sequelize, - modelName: 'LibraryItem' + modelName: 'libraryItem' }) - const { LibraryFolder } = sequelize.models - LibraryFolder.hasMany(LibraryItem) - LibraryItem.belongsTo(LibraryFolder) + const { libraryFolder, book, podcast } = sequelize.models + libraryFolder.hasMany(LibraryItem) + LibraryItem.belongsTo(libraryFolder) + + book.hasOne(LibraryItem, { + foreignKey: 'mediaId', + constraints: false, + scope: { + mediaType: 'book' + } + }) + LibraryItem.belongsTo(book, { foreignKey: 'mediaId', constraints: false }) + + podcast.hasOne(LibraryItem, { + foreignKey: 'mediaId', + constraints: false, + scope: { + mediaType: 'podcast' + } + }) + LibraryItem.belongsTo(podcast, { foreignKey: 'mediaId', constraints: false }) + + LibraryItem.addHook('afterFind', findResult => { + if (!findResult) return + + if (!Array.isArray(findResult)) findResult = [findResult] + for (const instance of findResult) { + if (instance.mediaType === 'book' && instance.book !== undefined) { + instance.media = instance.book + instance.dataValues.media = instance.dataValues.book + } else if (instance.mediaType === 'podcast' && instance.podcast !== undefined) { + instance.media = instance.podcast + instance.dataValues.media = instance.dataValues.podcast + } + // To prevent mistakes: + delete instance.book + delete instance.dataValues.book + delete instance.podcast + delete instance.dataValues.podcast + } + }) return LibraryItem } \ No newline at end of file diff --git a/server/models/LibrarySetting.js b/server/models/LibrarySetting.js index 3b5243fd..c45fc674 100644 --- a/server/models/LibrarySetting.js +++ b/server/models/LibrarySetting.js @@ -13,13 +13,13 @@ module.exports = (sequelize) => { value: DataTypes.STRING }, { sequelize, - modelName: 'LibrarySetting' + modelName: 'librarySetting' }) - const { Library } = sequelize.models + const { library } = sequelize.models - Library.hasMany(LibrarySetting) - LibrarySetting.belongsTo(Library) + library.hasMany(LibrarySetting) + LibrarySetting.belongsTo(library) return LibrarySetting } \ No newline at end of file diff --git a/server/models/MediaFile.js b/server/models/MediaFile.js index da0f1f60..f7fd0c1b 100644 --- a/server/models/MediaFile.js +++ b/server/models/MediaFile.js @@ -17,13 +17,13 @@ module.exports = (sequelize) => { tags: DataTypes.JSON }, { sequelize, - modelName: 'MediaFile' + modelName: 'mediaFile' }) - const { FileMetadata } = sequelize.models + const { fileMetadata } = sequelize.models - FileMetadata.hasOne(MediaFile, { foreignKey: 'FileMetadataId' }) - MediaFile.belongsTo(FileMetadata, { as: 'FileMetadata', foreignKey: 'FileMetadataId' }) + fileMetadata.hasOne(MediaFile, { foreignKey: 'fileMetadataId' }) + MediaFile.belongsTo(fileMetadata, { as: 'fileMetadata', foreignKey: 'fileMetadataId' }) return MediaFile } \ No newline at end of file diff --git a/server/models/MediaProgress.js b/server/models/MediaProgress.js index c45a136d..20256b01 100644 --- a/server/models/MediaProgress.js +++ b/server/models/MediaProgress.js @@ -8,7 +8,7 @@ module.exports = (sequelize) => { class MediaProgress extends Model { getMediaItem(options) { if (!this.mediaItemType) return Promise.resolve(null) - const mixinMethodName = `get${this.mediaItemType}` + const mixinMethodName = `get${sequelize.uppercaseFirst(this.mediaItemType)}` return this[mixinMethodName](options) } } @@ -19,7 +19,7 @@ module.exports = (sequelize) => { defaultValue: DataTypes.UUIDV4, primaryKey: true }, - MediaItemId: DataTypes.UUIDV4, + mediaItemId: DataTypes.UUIDV4, mediaItemType: DataTypes.STRING, duration: DataTypes.FLOAT, currentTime: DataTypes.FLOAT, @@ -28,46 +28,52 @@ module.exports = (sequelize) => { finishedAt: DataTypes.DATE }, { sequelize, - modelName: 'MediaProgress' + modelName: 'mediaProgress' }) - const { Book, PodcastEpisode, User } = sequelize.models - Book.hasMany(MediaProgress, { - foreignKey: 'MediaItemId', - constraints: false, - scope: { - mediaItemType: 'Book' - } - }) - MediaProgress.belongsTo(Book, { foreignKey: 'MediaItemId', constraints: false }) + const { book, podcastEpisode, user } = sequelize.models - PodcastEpisode.hasMany(MediaProgress, { - foreignKey: 'MediaItemId', + book.hasMany(MediaProgress, { + foreignKey: 'mediaItemId', constraints: false, scope: { - mediaItemType: 'PodcastEpisode' + mediaItemType: 'book' } }) - MediaProgress.belongsTo(PodcastEpisode, { foreignKey: 'MediaItemId', constraints: false }) + MediaProgress.belongsTo(book, { foreignKey: 'mediaItemId', constraints: false }) + + podcastEpisode.hasMany(MediaProgress, { + foreignKey: 'mediaItemId', + constraints: false, + scope: { + mediaItemType: 'podcastEpisode' + } + }) + MediaProgress.belongsTo(podcastEpisode, { foreignKey: 'mediaItemId', constraints: false }) MediaProgress.addHook('afterFind', findResult => { + if (!findResult) return + if (!Array.isArray(findResult)) findResult = [findResult] + for (const instance of findResult) { - if (instance.mediaItemType === 'Book' && instance.Book !== undefined) { - instance.MediaItem = instance.Book - } else if (instance.mediaItemType === 'PodcastEpisode' && instance.PodcastEpisode !== undefined) { - instance.MediaItem = instance.PodcastEpisode + if (instance.mediaItemType === 'book' && instance.book !== undefined) { + instance.mediaItem = instance.book + instance.dataValues.mediaItem = instance.dataValues.book + } else if (instance.mediaItemType === 'podcastEpisode' && instance.podcastEpisode !== undefined) { + instance.mediaItem = instance.podcastEpisode + instance.dataValues.mediaItem = instance.dataValues.podcastEpisode } // To prevent mistakes: - delete instance.Book - delete instance.dataValues.Book - delete instance.PodcastEpisode - delete instance.dataValues.PodcastEpisode + delete instance.book + delete instance.dataValues.book + delete instance.podcastEpisode + delete instance.dataValues.podcastEpisode } }) - User.hasMany(MediaProgress) - MediaProgress.belongsTo(User) + user.hasMany(MediaProgress) + MediaProgress.belongsTo(user) return MediaProgress } \ No newline at end of file diff --git a/server/models/MediaStream.js b/server/models/MediaStream.js index 2debf2c0..5faf96e8 100644 --- a/server/models/MediaStream.js +++ b/server/models/MediaStream.js @@ -37,13 +37,13 @@ module.exports = (sequelize) => { chapters: DataTypes.JSON }, { sequelize, - modelName: 'MediaStream' + modelName: 'mediaStream' }) - const { MediaFile } = sequelize.models + const { mediaFile } = sequelize.models - MediaFile.hasMany(MediaStream) - MediaStream.belongsTo(MediaFile) + mediaFile.hasMany(MediaStream) + MediaStream.belongsTo(mediaFile) return MediaStream } \ No newline at end of file diff --git a/server/models/Notification.js b/server/models/Notification.js index 485d169c..da0c19f9 100644 --- a/server/models/Notification.js +++ b/server/models/Notification.js @@ -22,7 +22,7 @@ module.exports = (sequelize) => { extraData: DataTypes.JSON }, { sequelize, - modelName: 'Notification' + modelName: 'notification' }) return Notification diff --git a/server/models/Person.js b/server/models/Person.js index e38b1cde..b1677085 100644 --- a/server/models/Person.js +++ b/server/models/Person.js @@ -15,12 +15,12 @@ module.exports = (sequelize) => { description: DataTypes.TEXT }, { sequelize, - modelName: 'Person' + modelName: 'person' }) - const { FileMetadata } = sequelize.models - FileMetadata.hasMany(Person, { foreignKey: 'ImageFileId' }) - Person.belongsTo(FileMetadata, { as: 'ImageFile', foreignKey: 'ImageFileId' }) // Ref: https://sequelize.org/docs/v6/core-concepts/assocs/#defining-an-alias + const { fileMetadata } = sequelize.models + fileMetadata.hasMany(Person, { foreignKey: 'imageFileId' }) + Person.belongsTo(fileMetadata, { as: 'imageFile', foreignKey: 'imageFileId' }) // Ref: https://sequelize.org/docs/v6/core-concepts/assocs/#defining-an-alias return Person } \ No newline at end of file diff --git a/server/models/PlaybackSession.js b/server/models/PlaybackSession.js index b471d913..3135143a 100644 --- a/server/models/PlaybackSession.js +++ b/server/models/PlaybackSession.js @@ -4,7 +4,7 @@ module.exports = (sequelize) => { class PlaybackSession extends Model { getMediaItem(options) { if (!this.mediaItemType) return Promise.resolve(null) - const mixinMethodName = `get${this.mediaItemType}` + const mixinMethodName = `get${sequelize.uppercaseFirst(this.mediaItemType)}` return this[mixinMethodName](options) } } @@ -15,7 +15,7 @@ module.exports = (sequelize) => { defaultValue: DataTypes.UUIDV4, primaryKey: true }, - MediaItemId: DataTypes.UUIDV4, + mediaItemId: DataTypes.UUIDV4, mediaItemType: DataTypes.STRING, displayTitle: DataTypes.STRING, displayAuthor: DataTypes.STRING, @@ -27,48 +27,53 @@ module.exports = (sequelize) => { serverVersion: DataTypes.STRING }, { sequelize, - modelName: 'PlaybackSession' + modelName: 'playbackSession' }) - const { Book, PodcastEpisode, User, Device } = sequelize.models + const { book, podcastEpisode, user, device } = sequelize.models - User.hasMany(PlaybackSession) - PlaybackSession.belongsTo(User) + user.hasMany(PlaybackSession) + PlaybackSession.belongsTo(user) - Device.hasMany(PlaybackSession) - PlaybackSession.belongsTo(Device) + device.hasMany(PlaybackSession) + PlaybackSession.belongsTo(device) - Book.hasMany(PlaybackSession, { - foreignKey: 'MediaItemId', + book.hasMany(PlaybackSession, { + foreignKey: 'mediaItemId', constraints: false, scope: { - mediaItemType: 'Book' + mediaItemType: 'book' } }) - PlaybackSession.belongsTo(Book, { foreignKey: 'MediaItemId', constraints: false }) + PlaybackSession.belongsTo(book, { foreignKey: 'mediaItemId', constraints: false }) - PodcastEpisode.hasOne(PlaybackSession, { - foreignKey: 'MediaItemId', + podcastEpisode.hasOne(PlaybackSession, { + foreignKey: 'mediaItemId', constraints: false, scope: { - mediaItemType: 'PodcastEpisode' + mediaItemType: 'podcastEpisode' } }) - PlaybackSession.belongsTo(PodcastEpisode, { foreignKey: 'MediaItemId', constraints: false }) + PlaybackSession.belongsTo(podcastEpisode, { foreignKey: 'mediaItemId', constraints: false }) PlaybackSession.addHook('afterFind', findResult => { + if (!findResult) return + if (!Array.isArray(findResult)) findResult = [findResult] + for (const instance of findResult) { - if (instance.mediaItemType === 'Book' && instance.Book !== undefined) { - instance.MediaItem = instance.Book - } else if (instance.mediaItemType === 'PodcastEpisode' && instance.PodcastEpisode !== undefined) { - instance.MediaItem = instance.PodcastEpisode + if (instance.mediaItemType === 'book' && instance.book !== undefined) { + instance.mediaItem = instance.book + instance.dataValues.mediaItem = instance.dataValues.book + } else if (instance.mediaItemType === 'podcastEpisode' && instance.podcastEpisode !== undefined) { + instance.mediaItem = instance.podcastEpisode + instance.dataValues.mediaItem = instance.dataValues.podcastEpisode } // To prevent mistakes: - delete instance.Book - delete instance.dataValues.Book - delete instance.PodcastEpisode - delete instance.dataValues.PodcastEpisode + delete instance.book + delete instance.dataValues.book + delete instance.podcastEpisode + delete instance.dataValues.podcastEpisode } }) diff --git a/server/models/PlaybackSessionListenTime.js b/server/models/PlaybackSessionListenTime.js index a3f581ad..12930903 100644 --- a/server/models/PlaybackSessionListenTime.js +++ b/server/models/PlaybackSessionListenTime.js @@ -13,13 +13,13 @@ module.exports = (sequelize) => { date: DataTypes.STRING }, { sequelize, - modelName: 'PlaybackSessionListenTime' + modelName: 'playbackSessionListenTime' }) - const { PlaybackSession } = sequelize.models + const { playbackSession } = sequelize.models - PlaybackSession.hasMany(PlaybackSessionListenTime) - PlaybackSessionListenTime.belongsTo(PlaybackSession) + playbackSession.hasMany(PlaybackSessionListenTime) + PlaybackSessionListenTime.belongsTo(playbackSession) return PlaybackSessionListenTime } \ No newline at end of file diff --git a/server/models/Playlist.js b/server/models/Playlist.js index 6fa0b4a9..43b99c56 100644 --- a/server/models/Playlist.js +++ b/server/models/Playlist.js @@ -13,15 +13,15 @@ module.exports = (sequelize) => { description: DataTypes.TEXT }, { sequelize, - modelName: 'Playlist' + modelName: 'playlist' }) - const { Library, User } = sequelize.models - Library.hasMany(Playlist) - Playlist.belongsTo(Library) + const { library, user } = sequelize.models + library.hasMany(Playlist) + Playlist.belongsTo(library) - User.hasMany(Playlist) - Playlist.belongsTo(User) + user.hasMany(Playlist) + Playlist.belongsTo(user) return Playlist } \ No newline at end of file diff --git a/server/models/PlaylistMediaItem.js b/server/models/PlaylistMediaItem.js index 0e6c14c8..dab3be97 100644 --- a/server/models/PlaylistMediaItem.js +++ b/server/models/PlaylistMediaItem.js @@ -4,7 +4,7 @@ module.exports = (sequelize) => { class PlaylistMediaItem extends Model { getMediaItem(options) { if (!this.mediaItemType) return Promise.resolve(null) - const mixinMethodName = `get${this.mediaItemType}` + const mixinMethodName = `get${sequelize.uppercaseFirst(this.mediaItemType)}` return this[mixinMethodName](options) } } @@ -15,53 +15,58 @@ module.exports = (sequelize) => { defaultValue: DataTypes.UUIDV4, primaryKey: true }, - MediaItemId: DataTypes.UUIDV4, + mediaItemId: DataTypes.UUIDV4, mediaItemType: DataTypes.STRING }, { sequelize, timestamps: true, updatedAt: false, - modelName: 'PlaylistMediaItem' + modelName: 'playlistMediaItem' }) - const { Book, PodcastEpisode, Playlist } = sequelize.models + const { book, podcastEpisode, playlist } = sequelize.models - Book.hasMany(PlaylistMediaItem, { - foreignKey: 'MediaItemId', + book.hasMany(PlaylistMediaItem, { + foreignKey: 'mediaItemId', constraints: false, scope: { - mediaItemType: 'Book' + mediaItemType: 'book' } }) - PlaylistMediaItem.belongsTo(Book, { foreignKey: 'MediaItemId', constraints: false }) + PlaylistMediaItem.belongsTo(book, { foreignKey: 'mediaItemId', constraints: false }) - PodcastEpisode.hasOne(PlaylistMediaItem, { - foreignKey: 'MediaItemId', + podcastEpisode.hasOne(PlaylistMediaItem, { + foreignKey: 'mediaItemId', constraints: false, scope: { - mediaItemType: 'PodcastEpisode' + mediaItemType: 'podcastEpisode' } }) - PlaylistMediaItem.belongsTo(PodcastEpisode, { foreignKey: 'MediaItemId', constraints: false }) + PlaylistMediaItem.belongsTo(podcastEpisode, { foreignKey: 'mediaItemId', constraints: false }) PlaylistMediaItem.addHook('afterFind', findResult => { + if (!findResult) return + if (!Array.isArray(findResult)) findResult = [findResult] + for (const instance of findResult) { - if (instance.mediaItemType === 'Book' && instance.Book !== undefined) { - instance.MediaItem = instance.Book - } else if (instance.mediaItemType === 'PodcastEpisode' && instance.PodcastEpisode !== undefined) { - instance.MediaItem = instance.PodcastEpisode + if (instance.mediaItemType === 'book' && instance.book !== undefined) { + instance.mediaItem = instance.book + instance.dataValues.mediaItem = instance.dataValues.book + } else if (instance.mediaItemType === 'podcastEpisode' && instance.podcastEpisode !== undefined) { + instance.mediaItem = instance.podcastEpisode + instance.dataValues.mediaItem = instance.dataValues.podcastEpisode } // To prevent mistakes: - delete instance.Book - delete instance.dataValues.Book - delete instance.PodcastEpisode - delete instance.dataValues.PodcastEpisode + delete instance.book + delete instance.dataValues.book + delete instance.podcastEpisode + delete instance.dataValues.podcastEpisode } }) - Playlist.hasMany(PlaylistMediaItem) - PlaylistMediaItem.belongsTo(Playlist) + playlist.hasMany(PlaylistMediaItem) + PlaylistMediaItem.belongsTo(playlist) return PlaylistMediaItem } \ No newline at end of file diff --git a/server/models/Podcast.js b/server/models/Podcast.js index cfb3f2dd..0a1f378a 100644 --- a/server/models/Podcast.js +++ b/server/models/Podcast.js @@ -9,7 +9,6 @@ module.exports = (sequelize) => { defaultValue: DataTypes.UUIDV4, primaryKey: true }, - // Metadata title: DataTypes.STRING, author: DataTypes.STRING, releaseDate: DataTypes.STRING, @@ -32,15 +31,13 @@ module.exports = (sequelize) => { lastCoverSearch: DataTypes.DATE }, { sequelize, - modelName: 'Podcast' + modelName: 'podcast' }) - const { LibraryItem, FileMetadata } = sequelize.models - LibraryItem.hasOne(Podcast) - Podcast.belongsTo(LibraryItem) + const { fileMetadata } = sequelize.models - FileMetadata.hasOne(Podcast, { foreignKey: 'ImageFileId' }) - Podcast.belongsTo(FileMetadata, { as: 'ImageFile', foreignKey: 'ImageFileId' }) // Ref: https://sequelize.org/docs/v6/core-concepts/assocs/#defining-an-alias + fileMetadata.hasOne(Podcast, { foreignKey: 'imageFileId' }) + Podcast.belongsTo(fileMetadata, { as: 'imageFile', foreignKey: 'imageFileId' }) // Ref: https://sequelize.org/docs/v6/core-concepts/assocs/#defining-an-alias return Podcast } \ No newline at end of file diff --git a/server/models/PodcastEpisode.js b/server/models/PodcastEpisode.js index 355fca7e..c3bf8e4b 100644 --- a/server/models/PodcastEpisode.js +++ b/server/models/PodcastEpisode.js @@ -23,12 +23,12 @@ module.exports = (sequelize) => { publishedAt: DataTypes.DATE }, { sequelize, - modelName: 'PodcastEpisode' + modelName: 'podcastEpisode' }) - const { Podcast } = sequelize.models - Podcast.hasMany(PodcastEpisode) - PodcastEpisode.belongsTo(Podcast) + const { podcast } = sequelize.models + podcast.hasMany(PodcastEpisode) + PodcastEpisode.belongsTo(podcast) return PodcastEpisode } \ No newline at end of file diff --git a/server/models/PodcastGenre.js b/server/models/PodcastGenre.js index dec9fc6d..c883572b 100644 --- a/server/models/PodcastGenre.js +++ b/server/models/PodcastGenre.js @@ -11,21 +11,21 @@ module.exports = (sequelize) => { } }, { sequelize, - modelName: 'PodcastGenre', + modelName: 'podcastGenre', timestamps: 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 - const { Podcast, Genre } = sequelize.models - Podcast.belongsToMany(Genre, { through: PodcastGenre }) - Genre.belongsToMany(Podcast, { through: PodcastGenre }) + const { podcast, genre } = sequelize.models + podcast.belongsToMany(genre, { through: PodcastGenre }) + genre.belongsToMany(podcast, { through: PodcastGenre }) - Podcast.hasMany(PodcastGenre) - PodcastGenre.belongsTo(Podcast) + podcast.hasMany(PodcastGenre) + PodcastGenre.belongsTo(podcast) - Genre.hasMany(PodcastGenre) - PodcastGenre.belongsTo(Genre) + genre.hasMany(PodcastGenre) + PodcastGenre.belongsTo(genre) return PodcastGenre } \ No newline at end of file diff --git a/server/models/PodcastTag.js b/server/models/PodcastTag.js index 4a6c54a0..64434376 100644 --- a/server/models/PodcastTag.js +++ b/server/models/PodcastTag.js @@ -11,21 +11,21 @@ module.exports = (sequelize) => { } }, { sequelize, - modelName: 'PodcastTag', + modelName: 'podcastTag', timestamps: 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 - const { Podcast, Tag } = sequelize.models - Podcast.belongsToMany(Tag, { through: PodcastTag }) - Tag.belongsToMany(Podcast, { through: PodcastTag }) + const { podcast, tag } = sequelize.models + podcast.belongsToMany(tag, { through: PodcastTag }) + tag.belongsToMany(podcast, { through: PodcastTag }) - Podcast.hasMany(PodcastTag) - PodcastTag.belongsTo(Podcast) + podcast.hasMany(PodcastTag) + PodcastTag.belongsTo(podcast) - Tag.hasMany(PodcastTag) - PodcastTag.belongsTo(Tag) + tag.hasMany(PodcastTag) + PodcastTag.belongsTo(tag) return PodcastTag } \ No newline at end of file diff --git a/server/models/Series.js b/server/models/Series.js index 814b3691..89a3b3dd 100644 --- a/server/models/Series.js +++ b/server/models/Series.js @@ -13,7 +13,7 @@ module.exports = (sequelize) => { description: DataTypes.TEXT }, { sequelize, - modelName: 'Series' + modelName: 'series' }) return Series diff --git a/server/models/Setting.js b/server/models/Setting.js index 53767f58..bb3ecef3 100644 --- a/server/models/Setting.js +++ b/server/models/Setting.js @@ -12,7 +12,7 @@ module.exports = (sequelize) => { type: DataTypes.INTEGER }, { sequelize, - modelName: 'Setting' + modelName: 'setting' }) return Setting diff --git a/server/models/Tag.js b/server/models/Tag.js index 12ae4a54..e58912be 100644 --- a/server/models/Tag.js +++ b/server/models/Tag.js @@ -13,7 +13,7 @@ module.exports = (sequelize) => { cleanName: DataTypes.STRING }, { sequelize, - modelName: 'Tag' + modelName: 'tag' }) return Tag diff --git a/server/models/User.js b/server/models/User.js index e81cb353..398d45d7 100644 --- a/server/models/User.js +++ b/server/models/User.js @@ -26,7 +26,7 @@ module.exports = (sequelize) => { extraData: DataTypes.JSON }, { sequelize, - modelName: 'User' + modelName: 'user' }) return User diff --git a/server/models/UserPermission.js b/server/models/UserPermission.js index b600907d..e9ceabd8 100644 --- a/server/models/UserPermission.js +++ b/server/models/UserPermission.js @@ -13,13 +13,13 @@ module.exports = (sequelize) => { value: DataTypes.STRING }, { sequelize, - modelName: 'UserPermission' + modelName: 'userPermission' }) - const { User } = sequelize.models + const { user } = sequelize.models - User.hasMany(UserPermission) - UserPermission.belongsTo(User) + user.hasMany(UserPermission) + UserPermission.belongsTo(user) return UserPermission } \ No newline at end of file diff --git a/server/routers/ApiRouter2.js b/server/routers/ApiRouter2.js new file mode 100644 index 00000000..b740c31e --- /dev/null +++ b/server/routers/ApiRouter2.js @@ -0,0 +1,15 @@ +const express = require('express') +const LibraryItemController = require('../controllers2/LibraryItemController') + +class ApiRouter2 { + constructor(Server) { + this.router = express() + this.router.disable('x-powered-by') + this.init() + } + + init() { + this.router.get('/items/:id', LibraryItemController.get.bind(this)) + } +} +module.exports = ApiRouter2 diff --git a/server/utils/migrations/dbMigration.js b/server/utils/migrations/dbMigration.js index ca951ee2..bf4324a4 100644 --- a/server/utils/migrations/dbMigration.js +++ b/server/utils/migrations/dbMigration.js @@ -1,6 +1,5 @@ const Path = require('path') const uuidv4 = require("uuid").v4 -const package = require('../../../package.json') const { AudioMimeType } = require('../constants') const Logger = require('../../Logger') const Database = require('../../Database') @@ -20,49 +19,50 @@ const oldDbIdMap = { files: {}, // key is fullpath podcastEpisodes: {}, books: {}, // key is library item id + podcasts: {}, // key is library item id devices: {} // key is a json stringify of the old DeviceInfo data } const newRecords = { - User: [], - UserPermission: [], - Library: [], - LibraryFolder: [], - LibrarySetting: [], - FileMetadata: [], - Person: [], - LibraryItem: [], - LibraryFile: [], - EBookFile: [], - Book: [], - BookAuthor: [], - BookNarrator: [], - BookChapter: [], - Tag: [], - BookTag: [], - Genre: [], - BookGenre: [], - Series: [], - BookSeries: [], - Podcast: [], - PodcastTag: [], - PodcastGenre: [], - PodcastEpisode: [], - MediaProgress: [], - AudioBookmark: [], - MediaFile: [], - MediaStream: [], - AudioTrack: [], - Device: [], - PlaybackSession: [], - PlaybackSessionListenTime: [], - Collection: [], - CollectionBook: [], - Playlist: [], - PlaylistMediaItem: [], - Feed: [], - FeedEpisode: [], - Setting: [], - Notification: [] + user: [], + userPermission: [], + library: [], + libraryFolder: [], + librarySetting: [], + fileMetadata: [], + person: [], + eBookFile: [], + book: [], + podcast: [], + libraryItem: [], + libraryFile: [], + bookAuthor: [], + bookNarrator: [], + bookChapter: [], + tag: [], + bookTag: [], + genre: [], + bookGenre: [], + series: [], + bookSeries: [], + podcastTag: [], + podcastGenre: [], + podcastEpisode: [], + mediaProgress: [], + audioBookmark: [], + mediaFile: [], + mediaStream: [], + audioTrack: [], + device: [], + playbackSession: [], + playbackSessionListenTime: [], + collection: [], + collectionBook: [], + playlist: [], + playlistMediaItem: [], + feed: [], + feedEpisode: [], + setting: [], + notification: [] } function getDeviceInfoString(deviceInfo, UserId) { @@ -102,10 +102,10 @@ function migrateBook(oldLibraryItem, LibraryItem) { // // Migrate ImageFile // - let ImageFileId = null + let imageFileId = null if (oldBook.coverPath) { - ImageFileId = oldDbIdMap.files[oldBook.coverPath] || null - if (!ImageFileId) { + imageFileId = oldDbIdMap.files[oldBook.coverPath] || null + if (!imageFileId) { const FileMetadata = { id: uuidv4(), filename: Path.basename(oldBook.coverPath), @@ -114,26 +114,28 @@ function migrateBook(oldLibraryItem, LibraryItem) { createdAt: LibraryItem.createdAt, updatedAt: LibraryItem.updatedAt } - newRecords.FileMetadata.push(FileMetadata) + newRecords.fileMetadata.push(FileMetadata) oldDbIdMap.files[oldBook.coverPath] = FileMetadata.id - ImageFileId = FileMetadata.id + imageFileId = FileMetadata.id } } // // Migrate EBookFile // - let EBookFileId = null + let eBookFileId = null if (oldBook.ebookFile) { if (oldDbIdMap.files[oldBook.ebookFile.metadata?.path]) { + const ext = oldBook.ebookFile.metadata.ext || '' const EBookFile = { id: uuidv4(), - FileMetadataId: oldDbIdMap.files[oldBook.ebookFile.metadata?.path] + format: ext.toLowerCase().slice(1), + fileMetadataId: oldDbIdMap.files[oldBook.ebookFile.metadata.path] } - newRecords.EBookFile.push(EBookFile) - EBookFileId = EBookFile.id + newRecords.eBookFile.push(EBookFile) + eBookFileId = EBookFile.id } else { - Logger.warn(`[dbMigration] migrateBook: `) + Logger.warn(`[dbMigration] migrateBook: Unable to find ebook file`) } } @@ -156,11 +158,10 @@ function migrateBook(oldLibraryItem, LibraryItem) { lastCoverSearch: oldBook.lastCoverSearch, createdAt: LibraryItem.createdAt, updatedAt: LibraryItem.updatedAt, - ImageFileId, - EBookFileId, - LibraryItemId: LibraryItem.id, + imageFileId, + eBookFileId } - newRecords.Book.push(Book) + newRecords.book.push(Book) oldDbIdMap.books[oldLibraryItem.id] = Book.id // @@ -169,8 +170,8 @@ function migrateBook(oldLibraryItem, LibraryItem) { const oldAudioFiles = oldBook.audioFiles let startOffset = 0 for (const oldAudioFile of oldAudioFiles) { - const FileMetadataId = oldDbIdMap.files[oldAudioFile.metadata.path] - if (!FileMetadataId) { + const fileMetadataId = oldDbIdMap.files[oldAudioFile.metadata.path] + if (!fileMetadataId) { Logger.warn(`[dbMigration] migrateBook: File metadata not found for audio file "${oldAudioFile.metadata.path}"`) continue } @@ -187,9 +188,9 @@ function migrateBook(oldLibraryItem, LibraryItem) { tags: cleanAudioFileMetaTags(oldAudioFile.metaTags), createdAt: LibraryItem.createdAt, updatedAt: LibraryItem.updatedAt, - FileMetadataId + fileMetadataId } - newRecords.MediaFile.push(MediaFile) + newRecords.mediaFile.push(MediaFile) const MediaStream = { id: uuidv4(), @@ -207,9 +208,9 @@ function migrateBook(oldLibraryItem, LibraryItem) { chapters: oldAudioFile.chapters, createdAt: LibraryItem.createdAt, updatedAt: LibraryItem.updatedAt, - MediaFileId: MediaFile.id + mediaFileId: MediaFile.id } - newRecords.MediaStream.push(MediaStream) + newRecords.mediaStream.push(MediaStream) if (oldAudioFile.embeddedCoverArt) { const CoverMediaStream = { @@ -220,17 +221,17 @@ function migrateBook(oldLibraryItem, LibraryItem) { default: true, createdAt: LibraryItem.createdAt, updatedAt: LibraryItem.updatedAt, - MediaFileId: MediaFile.id + mediaFileId: MediaFile.id } - newRecords.MediaStream.push(CoverMediaStream) + newRecords.mediaStream.push(CoverMediaStream) } const include = !oldAudioFile.exclude && !oldAudioFile.invalid const AudioTrack = { id: uuidv4(), - MediaItemId: Book.id, - mediaItemType: 'Book', + mediaItemId: Book.id, + mediaItemType: 'book', index: oldAudioFile.index, startOffset: include ? startOffset : null, duration: oldAudioFile.duration, @@ -241,9 +242,9 @@ function migrateBook(oldLibraryItem, LibraryItem) { discNumber: oldAudioFile.discNumFromMeta || oldAudioFile.discNumFromFilename, createdAt: LibraryItem.createdAt, updatedAt: LibraryItem.updatedAt, - MediaFileId: MediaFile.id + mediaFileId: MediaFile.id } - newRecords.AudioTrack.push(AudioTrack) + newRecords.audioTrack.push(AudioTrack) if (include) { startOffset += AudioTrack.duration @@ -267,13 +268,13 @@ function migrateBook(oldLibraryItem, LibraryItem) { updatedAt: LibraryItem.updatedAt } tagId = Tag.id - newRecords.Tag.push(Tag) + newRecords.tag.push(Tag) } - newRecords.BookTag.push({ + newRecords.bookTag.push({ id: uuidv4(), - BookId: Book.id, - TagId: tagId + bookId: Book.id, + tagId }) } @@ -281,7 +282,7 @@ function migrateBook(oldLibraryItem, LibraryItem) { // Migrate BookChapters // for (const oldChapter of oldBook.chapters) { - newRecords.BookChapter.push({ + newRecords.bookChapter.push({ id: uuidv4(), index: oldChapter.id, start: oldChapter.start, @@ -289,7 +290,7 @@ function migrateBook(oldLibraryItem, LibraryItem) { title: oldChapter.title, createdAt: LibraryItem.createdAt, updatedAt: LibraryItem.updatedAt, - BookId: Book.id + bookId: Book.id }) } @@ -311,13 +312,13 @@ function migrateBook(oldLibraryItem, LibraryItem) { updatedAt: LibraryItem.updatedAt } genreId = Genre.id - newRecords.Genre.push(Genre) + newRecords.genre.push(Genre) } - newRecords.BookGenre.push({ + newRecords.bookGenre.push({ id: uuidv4(), - BookId: Book.id, - GenreId: genreId + bookId: Book.id, + genreId }) } @@ -326,10 +327,10 @@ function migrateBook(oldLibraryItem, LibraryItem) { // for (const oldBookAuthor of oldBook.metadata.authors) { if (oldDbIdMap.people[oldBookAuthor.id]) { - newRecords.BookAuthor.push({ + newRecords.bookAuthor.push({ id: uuidv4(), - PersonId: oldDbIdMap.people[oldBookAuthor.id], - BookId: Book.id + authorId: oldDbIdMap.people[oldBookAuthor.id], + bookId: Book.id }) } else { Logger.warn(`[dbMigration] migrateBook: Book author not found "${oldBookAuthor.name}"`) @@ -340,23 +341,23 @@ function migrateBook(oldLibraryItem, LibraryItem) { // Migrate BookNarrators // for (const oldBookNarrator of oldBook.metadata.narrators) { - let PersonId = oldDbIdMap.people[oldBookNarrator] - if (!PersonId) { + let personId = oldDbIdMap.people[oldBookNarrator] + if (!personId) { const Person = { id: uuidv4(), - type: 'Narrator', + type: 'narrator', name: oldBookNarrator, createdAt: LibraryItem.createdAt, updatedAt: LibraryItem.updatedAt } - newRecords.Person.push(Person) - PersonId = Person.id + newRecords.person.push(Person) + personId = Person.id } - newRecords.BookNarrator.push({ + newRecords.bookNarrator.push({ id: uuidv4(), - PersonId, - BookId: Book.id + narratorId: personId, + bookId: Book.id }) } @@ -368,10 +369,10 @@ function migrateBook(oldLibraryItem, LibraryItem) { const BookSeries = { id: uuidv4(), sequence: oldBookSeries.sequence, - SeriesId: oldDbIdMap.series[oldBookSeries.id], - BookId: Book.id + seriesId: oldDbIdMap.series[oldBookSeries.id], + bookId: Book.id } - newRecords.BookSeries.push(BookSeries) + newRecords.bookSeries.push(BookSeries) } else { Logger.warn(`[dbMigration] migrateBook: Series not found "${oldBookSeries.name}"`) } @@ -385,10 +386,10 @@ function migratePodcast(oldLibraryItem, LibraryItem) { // // Migrate ImageFile // - let ImageFileId = null + let imageFileId = null if (oldPodcast.coverPath) { - ImageFileId = oldDbIdMap.files[oldPodcast.coverPath] || null - if (!ImageFileId) { + imageFileId = oldDbIdMap.files[oldPodcast.coverPath] || null + if (!imageFileId) { const FileMetadata = { id: uuidv4(), filename: Path.basename(oldPodcast.coverPath), @@ -397,9 +398,9 @@ function migratePodcast(oldLibraryItem, LibraryItem) { createdAt: LibraryItem.createdAt, updatedAt: LibraryItem.updatedAt } - newRecords.FileMetadata.push(FileMetadata) + newRecords.fileMetadata.push(FileMetadata) oldDbIdMap.files[oldPodcast.coverPath] = FileMetadata.id - ImageFileId = FileMetadata.id + imageFileId = FileMetadata.id } } @@ -429,10 +430,10 @@ function migratePodcast(oldLibraryItem, LibraryItem) { lastCoverSearch: oldPodcast.lastCoverSearch, createdAt: LibraryItem.createdAt, updatedAt: LibraryItem.updatedAt, - ImageFileId, - LibraryItemId: LibraryItem.id + imageFileId, } - newRecords.Podcast.push(Podcast) + newRecords.podcast.push(Podcast) + oldDbIdMap.podcasts[oldLibraryItem.id] = Podcast.id // // Migrate Tags @@ -451,13 +452,13 @@ function migratePodcast(oldLibraryItem, LibraryItem) { updatedAt: LibraryItem.updatedAt } tagId = Tag.id - newRecords.Tag.push(Tag) + newRecords.tag.push(Tag) } - newRecords.PodcastTag.push({ + newRecords.podcastTag.push({ id: uuidv4(), - PodcastId: Podcast.id, - TagId: tagId + podcastId: Podcast.id, + tagId }) } @@ -478,13 +479,13 @@ function migratePodcast(oldLibraryItem, LibraryItem) { updatedAt: LibraryItem.updatedAt } genreId = Genre.id - newRecords.Genre.push(Genre) + newRecords.genre.push(Genre) } - newRecords.PodcastGenre.push({ + newRecords.podcastGenre.push({ id: uuidv4(), - PodcastId: Podcast.id, - GenreId: genreId + podcastId: Podcast.id, + genreId }) } @@ -494,8 +495,8 @@ function migratePodcast(oldLibraryItem, LibraryItem) { const oldEpisodes = oldPodcast.episodes || [] for (const oldEpisode of oldEpisodes) { const oldAudioFile = oldEpisode.audioFile - const FileMetadataId = oldDbIdMap.files[oldAudioFile.metadata.path] - if (!FileMetadataId) { + const fileMetadataId = oldDbIdMap.files[oldAudioFile.metadata.path] + if (!fileMetadataId) { Logger.warn(`[dbMigration] migratePodcast: File metadata not found for audio file "${oldAudioFile.metadata.path}"`) continue } @@ -516,9 +517,9 @@ function migratePodcast(oldLibraryItem, LibraryItem) { publishedAt: oldEpisode.publishedAt, createdAt: oldEpisode.addedAt, updatedAt: oldEpisode.updatedAt, - PodcastId: Podcast.id + podcastId: Podcast.id } - newRecords.PodcastEpisode.push(PodcastEpisode) + newRecords.podcastEpisode.push(PodcastEpisode) oldDbIdMap.podcastEpisodes[oldEpisode.id] = PodcastEpisode.id // @@ -535,9 +536,9 @@ function migratePodcast(oldLibraryItem, LibraryItem) { tags: cleanAudioFileMetaTags(oldAudioFile.metaTags), createdAt: LibraryItem.createdAt, updatedAt: LibraryItem.updatedAt, - FileMetadataId + fileMetadataId } - newRecords.MediaFile.push(MediaFile) + newRecords.mediaFile.push(MediaFile) const MediaStream = { id: uuidv4(), @@ -555,9 +556,9 @@ function migratePodcast(oldLibraryItem, LibraryItem) { chapters: oldAudioFile.chapters, createdAt: LibraryItem.createdAt, updatedAt: LibraryItem.updatedAt, - MediaFileId: MediaFile.id + mediaFileId: MediaFile.id } - newRecords.MediaStream.push(MediaStream) + newRecords.mediaStream.push(MediaStream) if (oldAudioFile.embeddedCoverArt) { const CoverMediaStream = { @@ -568,15 +569,15 @@ function migratePodcast(oldLibraryItem, LibraryItem) { default: true, createdAt: LibraryItem.createdAt, updatedAt: LibraryItem.updatedAt, - MediaFileId: MediaFile.id + mediaFileId: MediaFile.id } - newRecords.MediaStream.push(CoverMediaStream) + newRecords.mediaStream.push(CoverMediaStream) } const AudioTrack = { id: uuidv4(), - MediaItemId: Podcast.id, - mediaItemType: 'Podcast', + mediaItemId: PodcastEpisode.id, + mediaItemType: 'podcastEpisode', index: oldAudioFile.index, startOffset: 0, duration: oldAudioFile.duration, @@ -587,16 +588,16 @@ function migratePodcast(oldLibraryItem, LibraryItem) { discNumber: oldAudioFile.discNumFromMeta || oldAudioFile.discNumFromFilename, createdAt: LibraryItem.createdAt, updatedAt: LibraryItem.updatedAt, - MediaFileId: MediaFile.id + mediaFileId: MediaFile.id } - newRecords.AudioTrack.push(AudioTrack) + newRecords.audioTrack.push(AudioTrack) } } function migrateLibraryItems(oldLibraryItems) { for (const oldLibraryItem of oldLibraryItems) { - const LibraryId = oldDbIdMap.libraryFolders[oldLibraryItem.folderId] - if (!LibraryId) { + const libraryFolderId = oldDbIdMap.libraryFolders[oldLibraryItem.folderId] + if (!libraryFolderId) { Logger.error(`[dbMigration] migrateLibraryItems: Old library folder id not found "${oldLibraryItem.folderId}"`) continue } @@ -609,6 +610,7 @@ function migrateLibraryItems(oldLibraryItems) { ino: oldLibraryItem.ino, path: oldLibraryItem.path, relPath: oldLibraryItem.relPath, + mediaId: null, // set below mediaType: oldLibraryItem.mediaType, isFile: !!oldLibraryItem.isFile, isMissing: !!oldLibraryItem.isMissing, @@ -620,10 +622,10 @@ function migrateLibraryItems(oldLibraryItems) { lastScanVersion: oldLibraryItem.scanVersion, createdAt: oldLibraryItem.addedAt, updatedAt: oldLibraryItem.updatedAt, - LibraryId + libraryFolderId } oldDbIdMap.libraryItems[oldLibraryItem.id] = LibraryItem.id - newRecords.LibraryItem.push(LibraryItem) + newRecords.libraryItem.push(LibraryItem) // // Migrate LibraryFiles @@ -642,17 +644,17 @@ function migrateLibraryItems(oldLibraryItems) { createdAt: oldLibraryFile.addedAt || Date.now(), updatedAt: oldLibraryFile.updatedAt || Date.now() } - newRecords.FileMetadata.push(FileMetadata) + newRecords.fileMetadata.push(FileMetadata) oldDbIdMap.files[FileMetadata.path] = FileMetadata.id const LibraryFile = { id: uuidv4(), createdAt: FileMetadata.createdAt, updatedAt: FileMetadata.updatedAt, - FileMetadataId: FileMetadata.id, - LibraryItemId: LibraryItem.id + fileMetadataId: FileMetadata.id, + libraryItemId: LibraryItem.id } - newRecords.LibraryFile.push(LibraryFile) + newRecords.libraryFile.push(LibraryFile) } // @@ -660,8 +662,10 @@ function migrateLibraryItems(oldLibraryItems) { // if (oldLibraryItem.mediaType === 'book') { migrateBook(oldLibraryItem, LibraryItem) + LibraryItem.mediaId = oldDbIdMap.books[oldLibraryItem.id] } else if (oldLibraryItem.mediaType === 'podcast') { migratePodcast(oldLibraryItem, LibraryItem) + LibraryItem.mediaId = oldDbIdMap.podcasts[oldLibraryItem.id] } } } @@ -682,20 +686,20 @@ function migrateLibraries(oldLibraries) { updatedAt: oldLibrary.lastUpdate } oldDbIdMap.libraries[oldLibrary.id] = Library.id - newRecords.Library.push(Library) + newRecords.library.push(Library) // // Migrate LibrarySettings // const oldLibrarySettings = oldLibrary.settings || {} for (const oldSettingsKey in oldLibrarySettings) { - newRecords.LibrarySetting.push({ + newRecords.librarySetting.push({ id: uuidv4(), key: oldSettingsKey, value: oldLibrarySettings[oldSettingsKey], createdAt: oldLibrary.createdAt, updatedAt: oldLibrary.lastUpdate, - LibraryId: Library.id + libraryId: Library.id }) } @@ -708,10 +712,10 @@ function migrateLibraries(oldLibraries) { path: oldFolder.fullPath, createdAt: oldFolder.addedAt, updatedAt: oldLibrary.lastUpdate, - LibraryId: Library.id + libraryId: Library.id } oldDbIdMap.libraryFolders[oldFolder.id] = LibraryFolder.id - newRecords.LibraryFolder.push(LibraryFolder) + newRecords.libraryFolder.push(LibraryFolder) } } } @@ -728,22 +732,22 @@ function migrateAuthors(oldAuthors) { createdAt: oldAuthor.addedAt || Date.now(), updatedAt: oldAuthor.updatedAt || Date.now() } - newRecords.FileMetadata.push(FileMetadata) + newRecords.fileMetadata.push(FileMetadata) imageFileId = FileMetadata.id } const Person = { id: uuidv4(), - type: 'Author', + type: 'author', name: oldAuthor.name, asin: oldAuthor.asin || null, description: oldAuthor.description, createdAt: oldAuthor.addedAt || Date.now(), updatedAt: oldAuthor.updatedAt || Date.now(), - ImageFileId: imageFileId + imageFileId } oldDbIdMap.people[oldAuthor.id] = Person.id - newRecords.Person.push(Person) + newRecords.person.push(Person) } } @@ -757,7 +761,7 @@ function migrateSeries(oldSerieses) { updatedAt: oldSeries.updatedAt || Date.now() } oldDbIdMap.series[oldSeries.id] = Series.id - newRecords.Series.push(Series) + newRecords.series.push(Series) } } @@ -780,7 +784,7 @@ function migrateUsers(oldUsers) { createdAt: oldUser.createdAt || Date.now() } oldDbIdMap.users[oldUser.id] = User.id - newRecords.User.push(User) + newRecords.user.push(User) // // Migrate UserPermissions @@ -792,9 +796,9 @@ function migrateUsers(oldUsers) { key: oldUserPermission, value: !!oldUser.permissions[oldUserPermission], createdAt: User.createdAt, - UserId: User.id + userId: User.id } - newRecords.UserPermission.push(UserPermission) + newRecords.userPermission.push(UserPermission) } } if (oldUser.librariesAccessible?.length) { @@ -803,9 +807,9 @@ function migrateUsers(oldUsers) { key: 'librariesAccessible', value: JSON.stringify(oldUser.librariesAccessible), createdAt: User.createdAt, - UserId: User.id + userId: User.id } - newRecords.UserPermission.push(UserPermission) + newRecords.userPermission.push(UserPermission) } if (oldUser.itemTagsAccessible?.length) { const UserPermission = { @@ -813,32 +817,32 @@ function migrateUsers(oldUsers) { key: 'itemTagsAccessible', value: JSON.stringify(oldUser.itemTagsAccessible), createdAt: User.createdAt, - UserId: User.id + userId: User.id } - newRecords.UserPermission.push(UserPermission) + newRecords.userPermission.push(UserPermission) } // // Migrate MediaProgress // for (const oldMediaProgress of oldUser.mediaProgress) { - let mediaItemType = 'Book' - let MediaItemId = null + let mediaItemType = 'book' + let mediaItemId = null if (oldMediaProgress.episodeId) { - mediaItemType = 'PodcastEpisode' - MediaItemId = oldDbIdMap.podcastEpisodes[oldMediaProgress.episodeId] + mediaItemType = 'podcastEpisode' + mediaItemId = oldDbIdMap.podcastEpisodes[oldMediaProgress.episodeId] } else { - MediaItemId = oldDbIdMap.books[oldMediaProgress.libraryItemId] + mediaItemId = oldDbIdMap.books[oldMediaProgress.libraryItemId] } - if (!MediaItemId) { + if (!mediaItemId) { Logger.warn(`[dbMigration] migrateUsers: Unable to find media item for media progress "${oldMediaProgress.id}"`) continue } const MediaProgress = { id: uuidv4(), - MediaItemId, + mediaItemId, mediaItemType, duration: oldMediaProgress.duration, currentTime: oldMediaProgress.currentTime, @@ -847,49 +851,49 @@ function migrateUsers(oldUsers) { finishedAt: oldMediaProgress.finishedAt, createdAt: oldMediaProgress.startedAt || oldMediaProgress.lastUpdate, updatedAt: oldMediaProgress.lastUpdate, - UserId: User.id + userId: User.id } - newRecords.MediaProgress.push(MediaProgress) + newRecords.mediaProgress.push(MediaProgress) } // // Migrate AudioBookmarks // for (const oldBookmark of oldUser.bookmarks) { - const MediaItemId = oldDbIdMap.books[oldBookmark.libraryItemId] - if (!MediaItemId) { + const mediaItemId = oldDbIdMap.books[oldBookmark.libraryItemId] + if (!mediaItemId) { Logger.warn(`[dbMigration] migrateUsers: Unable to find media item for audio bookmark "${oldBookmark.id}"`) continue } const AudioBookmark = { id: uuidv4(), - MediaItemId, - mediaItemType: 'Book', + mediaItemId, + mediaItemType: 'book', title: oldBookmark.title, time: oldBookmark.time, createdAt: oldBookmark.createdAt, updatedAt: oldBookmark.createdAt, - UserId: User.id + userId: User.id } - newRecords.AudioBookmark.push(AudioBookmark) + newRecords.audioBookmark.push(AudioBookmark) } } } function migrateSessions(oldSessions) { for (const oldSession of oldSessions) { - const UserId = oldDbIdMap.users[oldSession.userId] || null // Can be null + const userId = oldDbIdMap.users[oldSession.userId] || null // Can be null // // Migrate Device // - let DeviceId = null + let deviceId = null if (oldSession.deviceInfo) { const oldDeviceInfo = oldSession.deviceInfo - const deviceInfoStr = getDeviceInfoString(oldDeviceInfo, UserId) - DeviceId = oldDbIdMap.devices[deviceInfoStr] - if (!DeviceId) { + const deviceInfoStr = getDeviceInfoString(oldDeviceInfo, userId) + deviceId = oldDbIdMap.devices[deviceInfoStr] + if (!deviceId) { let clientName = 'Unknown' let clientVersion = null let deviceName = null @@ -918,9 +922,9 @@ function migrateSessions(oldSessions) { ipAddress: oldDeviceInfo.ipAddress, deviceName, // e.g. Windows 10 Chrome, Google Pixel 6, Apple iPhone 10,3 deviceVersion, - UserId + userId } - newRecords.Device.push(Device) + newRecords.device.push(Device) oldDbIdMap.devices[deviceInfoStr] = Device.id } } @@ -929,18 +933,18 @@ function migrateSessions(oldSessions) { // // Migrate PlaybackSession // - let MediaItemId = null - let mediaItemType = 'Book' + let mediaItemId = null + let mediaItemType = 'book' if (oldSession.mediaType === 'podcast') { - MediaItemId = oldDbIdMap.podcastEpisodes[oldSession.episodeId] || null - mediaItemType = 'PodcastEpisode' + mediaItemId = oldDbIdMap.podcastEpisodes[oldSession.episodeId] || null + mediaItemType = 'podcastEpisode' } else { - MediaItemId = oldDbIdMap.books[oldSession.libraryItemId] || null + mediaItemId = oldDbIdMap.books[oldSession.libraryItemId] || null } const PlaybackSession = { id: uuidv4(), - MediaItemId, // Can be null + mediaItemId, // Can be null mediaItemType, displayTitle: oldSession.displayTitle, displayAuthor: oldSession.displayAuthor, @@ -952,10 +956,10 @@ function migrateSessions(oldSessions) { serverVersion: oldSession.deviceInfo?.serverVersion || null, createdAt: oldSession.startedAt, updatedAt: oldSession.updatedAt, - UserId, // Can be null - DeviceId + userId, // Can be null + deviceId } - newRecords.PlaybackSession.push(PlaybackSession) + newRecords.playbackSession.push(PlaybackSession) if (oldSession.timeListening) { const PlaybackSessionListenTime = { @@ -964,17 +968,17 @@ function migrateSessions(oldSessions) { date: oldSession.date || dateAndTime.format(new Date(PlaybackSession.createdAt), 'YYYY-MM-DD'), createdAt: PlaybackSession.createdAt, updatedAt: PlaybackSession.updatedAt, - PlaybackSessionId: PlaybackSession.id + playbackSessionId: PlaybackSession.id } - newRecords.PlaybackSessionListenTime.push(PlaybackSessionListenTime) + newRecords.playbackSessionListenTime.push(PlaybackSessionListenTime) } } } function migrateCollections(oldCollections) { for (const oldCollection of oldCollections) { - const LibraryId = oldDbIdMap.libraries[oldCollection.libraryId] - if (!LibraryId) { + const libraryId = oldDbIdMap.libraries[oldCollection.libraryId] + if (!libraryId) { Logger.warn(`[dbMigration] migrateCollections: Library not found for collection "${oldCollection.name}" (id:${oldCollection.libraryId})`) continue } @@ -991,42 +995,42 @@ function migrateCollections(oldCollections) { description: oldCollection.description, createdAt: oldCollection.createdAt, updatedAt: oldCollection.lastUpdate, - LibraryId + libraryId } oldDbIdMap.collections[oldCollection.id] = Collection.id - newRecords.Collection.push(Collection) + newRecords.collection.push(Collection) - BookIds.forEach((BookId) => { + BookIds.forEach((bookId) => { const CollectionBook = { id: uuidv4(), createdAt: Collection.createdAt, - BookId, - CollectionId: Collection.id + bookId, + collectionId: Collection.id } - newRecords.CollectionBook.push(CollectionBook) + newRecords.collectionBook.push(CollectionBook) }) } } function migratePlaylists(oldPlaylists) { for (const oldPlaylist of oldPlaylists) { - const LibraryId = oldDbIdMap.libraries[oldPlaylist.libraryId] - if (!LibraryId) { + const libraryId = oldDbIdMap.libraries[oldPlaylist.libraryId] + if (!libraryId) { Logger.warn(`[dbMigration] migratePlaylists: Library not found for playlist "${oldPlaylist.name}" (id:${oldPlaylist.libraryId})`) continue } - const UserId = oldDbIdMap.users[oldPlaylist.userId] - if (!UserId) { + const userId = oldDbIdMap.users[oldPlaylist.userId] + if (!userId) { Logger.warn(`[dbMigration] migratePlaylists: User not found for playlist "${oldPlaylist.name}" (id:${oldPlaylist.userId})`) continue } - let mediaItemType = 'Book' + let mediaItemType = 'book' let MediaItemIds = [] oldPlaylist.items.forEach((itemObj) => { if (itemObj.episodeId) { - mediaItemType = 'PodcastEpisode' + mediaItemType = 'podcastEpisode' if (oldDbIdMap.podcastEpisodes[itemObj.episodeId]) { MediaItemIds.push(oldDbIdMap.podcastEpisodes[itemObj.episodeId]) } @@ -1045,20 +1049,20 @@ function migratePlaylists(oldPlaylists) { description: oldPlaylist.description, createdAt: oldPlaylist.createdAt, updatedAt: oldPlaylist.lastUpdate, - UserId, - LibraryId + userId, + libraryId } - newRecords.Playlist.push(Playlist) + newRecords.playlist.push(Playlist) - MediaItemIds.forEach((MediaItemId) => { + MediaItemIds.forEach((mediaItemId) => { const PlaylistMediaItem = { id: uuidv4(), - MediaItemId, + mediaItemId, mediaItemType, createdAt: Playlist.createdAt, - PlaylistId: Playlist.id + playlistId: Playlist.id } - newRecords.PlaylistMediaItem.push(PlaylistMediaItem) + newRecords.playlistMediaItem.push(PlaylistMediaItem) }) } } @@ -1069,27 +1073,23 @@ function migrateFeeds(oldFeeds) { continue } - let entityType = null - let EntityId = null + let entityId = null if (oldFeed.entityType === 'collection') { - entityType = 'Collection' - EntityId = oldDbIdMap.collections[oldFeed.entityId] + entityId = oldDbIdMap.collections[oldFeed.entityId] } else if (oldFeed.entityType === 'libraryItem') { - entityType = 'LibraryItem' - EntityId = oldDbIdMap.libraryItems[oldFeed.entityId] + entityId = oldDbIdMap.libraryItems[oldFeed.entityId] } else if (oldFeed.entityType === 'series') { - entityType = 'Series' - EntityId = oldDbIdMap.series[oldFeed.entityId] + entityId = oldDbIdMap.series[oldFeed.entityId] } - if (!EntityId) { - Logger.warn(`[dbMigration] migrateFeeds: Entity not found for feed "${entityType}" (id:${oldFeed.entityId})`) + if (!entityId) { + Logger.warn(`[dbMigration] migrateFeeds: Entity not found for feed "${oldFeed.entityType}" (id:${oldFeed.entityId})`) continue } - const UserId = oldDbIdMap.users[oldFeed.userId] - if (!UserId) { + const userId = oldDbIdMap.users[oldFeed.userId] + if (!userId) { Logger.warn(`[dbMigration] migrateFeeds: User not found for feed (id:${oldFeed.userId})`) continue } @@ -1099,8 +1099,8 @@ function migrateFeeds(oldFeeds) { const Feed = { id: uuidv4(), slug: oldFeed.slug, - entityType, - EntityId, + entityType: oldFeed.entityType, + entityId, entityUpdatedAt: oldFeed.entityUpdatedAt, serverAddress: oldFeed.serverAddress, feedURL: oldFeed.feedUrl, @@ -1117,9 +1117,9 @@ function migrateFeeds(oldFeeds) { preventIndexing: !!oldFeedMeta.preventIndexing, createdAt: oldFeed.createdAt, updatedAt: oldFeed.updatedAt, - UserId + userId } - newRecords.Feed.push(Feed) + newRecords.feed.push(Feed) // // Migrate FeedEpisodes @@ -1143,9 +1143,9 @@ function migrateFeeds(oldFeeds) { explicit: !!oldFeedEpisode.explicit, createdAt: oldFeed.createdAt, updatedAt: oldFeed.updatedAt, - FeedId: Feed.id + feedId: Feed.id } - newRecords.FeedEpisode.push(FeedEpisode) + newRecords.feedEpisode.push(FeedEpisode) } } } @@ -1162,7 +1162,7 @@ function migrateSettings(oldSettings) { if (value === undefined) value = null else if (serverSettingsKey === 'sortingPrefixes') value = JSON.stringify(value) - newRecords.Setting.push({ + newRecords.setting.push({ key: serverSettingsKey, value, type: 0 @@ -1178,7 +1178,7 @@ function migrateSettings(oldSettings) { notificationDelay: notificationSettings.notificationDelay ?? 1000 // ms delay between firing notifications } for (const notificationSettingKey in cleanedCopy) { - newRecords.Setting.push({ + newRecords.setting.push({ key: notificationSettingKey, value: cleanedCopy[notificationSettingKey], type: 1 @@ -1206,7 +1206,7 @@ function migrateSettings(oldSettings) { createdAt: oldNotification.createdAt, updatedAt: oldNotification.createdAt } - newRecords.Notification.push(Notification) + newRecords.notification.push(Notification) } } }