mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Adding more models
This commit is contained in:
		
							parent
							
								
									adc4309951
								
							
						
					
					
						commit
						bbf324ea83
					
				@ -37,6 +37,31 @@ class Database {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  buildModels() {
 | 
					  buildModels() {
 | 
				
			||||||
    require('./models/User')(this.sequelize)
 | 
					    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/PodcastEpisode')(this.sequelize)
 | 
				
			||||||
 | 
					    require('./models/MediaProgress')(this.sequelize)
 | 
				
			||||||
 | 
					    require('./models/LibraryFile')(this.sequelize)
 | 
				
			||||||
 | 
					    require('./models/Person')(this.sequelize)
 | 
				
			||||||
 | 
					    require('./models/AudioBookmark')(this.sequelize)
 | 
				
			||||||
 | 
					    require('./models/AudioTrack')(this.sequelize)
 | 
				
			||||||
 | 
					    require('./models/BookAuthor')(this.sequelize)
 | 
				
			||||||
 | 
					    require('./models/BookChapter')(this.sequelize)
 | 
				
			||||||
 | 
					    require('./models/Genre')(this.sequelize)
 | 
				
			||||||
 | 
					    require('./models/BookGenre')(this.sequelize)
 | 
				
			||||||
 | 
					    require('./models/BookNarrator')(this.sequelize)
 | 
				
			||||||
 | 
					    require('./models/Series')(this.sequelize)
 | 
				
			||||||
 | 
					    require('./models/BookSeries')(this.sequelize)
 | 
				
			||||||
 | 
					    require('./models/Tag')(this.sequelize)
 | 
				
			||||||
 | 
					    require('./models/BookTag')(this.sequelize)
 | 
				
			||||||
 | 
					    require('./models/PodcastTag')(this.sequelize)
 | 
				
			||||||
 | 
					    require('./models/Collection')(this.sequelize)
 | 
				
			||||||
 | 
					    require('./models/CollectionBook')(this.sequelize)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return this.sequelize.sync()
 | 
					    return this.sequelize.sync()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										73
									
								
								server/models/AudioBookmark.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								server/models/AudioBookmark.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,73 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const uppercaseFirst = str => `${str[0].toUpperCase()}${str.substr(1)}`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Polymorphic association: https://sequelize.org/docs/v6/advanced-association-concepts/polymorphic-associations/
 | 
				
			||||||
 | 
					 * Book has many AudioBookmark. PodcastEpisode has many AudioBookmark.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class AudioBookmark extends Model {
 | 
				
			||||||
 | 
					    getMediaItem(options) {
 | 
				
			||||||
 | 
					      if (!this.mediaItemType) return Promise.resolve(null)
 | 
				
			||||||
 | 
					      const mixinMethodName = `get${uppercaseFirst(this.mediaItemType)}`
 | 
				
			||||||
 | 
					      return this[mixinMethodName](options)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  AudioBookmark.init({
 | 
				
			||||||
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    mediaItemId: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					    mediaItemType: DataTypes.STRING,
 | 
				
			||||||
 | 
					    title: DataTypes.STRING,
 | 
				
			||||||
 | 
					    time: DataTypes.INTEGER
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    modelName: 'AudioBookmark'
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const { User, Book, PodcastEpisode } = sequelize.models
 | 
				
			||||||
 | 
					  Book.hasMany(AudioBookmark, {
 | 
				
			||||||
 | 
					    foreignKey: 'mediaItemId',
 | 
				
			||||||
 | 
					    constraints: false,
 | 
				
			||||||
 | 
					    scope: {
 | 
				
			||||||
 | 
					      mediaItemType: 'book'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  AudioBookmark.belongsTo(Book, { foreignKey: 'mediaItemId', constraints: false })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  PodcastEpisode.hasMany(AudioBookmark, {
 | 
				
			||||||
 | 
					    foreignKey: 'mediaItemId',
 | 
				
			||||||
 | 
					    constraints: false,
 | 
				
			||||||
 | 
					    scope: {
 | 
				
			||||||
 | 
					      mediaItemType: 'podcastEpisode'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  AudioBookmark.belongsTo(PodcastEpisode, { foreignKey: 'mediaItemId', constraints: false })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  AudioBookmark.addHook('afterFind', findResult => {
 | 
				
			||||||
 | 
					    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
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      // To prevent mistakes:
 | 
				
			||||||
 | 
					      delete instance.book
 | 
				
			||||||
 | 
					      delete instance.dataValues.book
 | 
				
			||||||
 | 
					      delete instance.podcastEpisode
 | 
				
			||||||
 | 
					      delete instance.dataValues.podcastEpisode
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  User.hasMany(AudioBookmark)
 | 
				
			||||||
 | 
					  AudioBookmark.belongsTo(User)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return AudioBookmark
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										71
									
								
								server/models/AudioTrack.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								server/models/AudioTrack.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,71 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const uppercaseFirst = str => `${str[0].toUpperCase()}${str.substr(1)}`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Polymorphic association: https://sequelize.org/docs/v6/advanced-association-concepts/polymorphic-associations/
 | 
				
			||||||
 | 
					 * Book has many AudioTrack. PodcastEpisode has one AudioTrack.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class AudioTrack extends Model {
 | 
				
			||||||
 | 
					    getMediaItem(options) {
 | 
				
			||||||
 | 
					      if (!this.mediaItemType) return Promise.resolve(null)
 | 
				
			||||||
 | 
					      const mixinMethodName = `get${uppercaseFirst(this.mediaItemType)}`
 | 
				
			||||||
 | 
					      return this[mixinMethodName](options)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  AudioTrack.init({
 | 
				
			||||||
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    mediaItemId: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					    mediaItemType: DataTypes.STRING,
 | 
				
			||||||
 | 
					    index: DataTypes.INTEGER
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    modelName: 'AudioTrack'
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const { Book, PodcastEpisode, FileMetadata } = sequelize.models
 | 
				
			||||||
 | 
					  Book.hasMany(AudioTrack, {
 | 
				
			||||||
 | 
					    foreignKey: 'mediaItemId',
 | 
				
			||||||
 | 
					    constraints: false,
 | 
				
			||||||
 | 
					    scope: {
 | 
				
			||||||
 | 
					      mediaItemType: 'book'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  AudioTrack.belongsTo(Book, { foreignKey: 'mediaItemId', constraints: false })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  PodcastEpisode.hasOne(AudioTrack, {
 | 
				
			||||||
 | 
					    foreignKey: 'mediaItemId',
 | 
				
			||||||
 | 
					    constraints: false,
 | 
				
			||||||
 | 
					    scope: {
 | 
				
			||||||
 | 
					      mediaItemType: 'podcastEpisode'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  AudioTrack.belongsTo(PodcastEpisode, { foreignKey: 'mediaItemId', constraints: false })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  AudioTrack.addHook('afterFind', findResult => {
 | 
				
			||||||
 | 
					    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
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      // To prevent mistakes:
 | 
				
			||||||
 | 
					      delete instance.book
 | 
				
			||||||
 | 
					      delete instance.dataValues.book
 | 
				
			||||||
 | 
					      delete instance.podcastEpisode
 | 
				
			||||||
 | 
					      delete instance.dataValues.podcastEpisode
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  FileMetadata.hasOne(AudioTrack)
 | 
				
			||||||
 | 
					  AudioTrack.belongsTo(FileMetadata)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return AudioTrack
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										40
									
								
								server/models/Book.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								server/models/Book.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class Book extends Model { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Book.init({
 | 
				
			||||||
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    title: 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,
 | 
				
			||||||
 | 
					    lastCoverSearchQuery: DataTypes.STRING,
 | 
				
			||||||
 | 
					    lastCoverSearch: DataTypes.DATE
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    modelName: 'Book'
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const { LibraryItem, FileMetadata, EBookFile } = sequelize.models
 | 
				
			||||||
 | 
					  LibraryItem.hasOne(Book)
 | 
				
			||||||
 | 
					  Book.belongsTo(LibraryItem)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  FileMetadata.hasOne(Book)
 | 
				
			||||||
 | 
					  Book.belongsTo(FileMetadata, { as: 'ImageFile' }) // Ref: https://sequelize.org/docs/v6/core-concepts/assocs/#defining-an-alias
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  EBookFile.hasOne(Book)
 | 
				
			||||||
 | 
					  Book.belongsTo(EBookFile)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return Book
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										31
									
								
								server/models/BookAuthor.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								server/models/BookAuthor.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class BookAuthor extends Model { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  BookAuthor.init({
 | 
				
			||||||
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    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 })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Book.hasMany(BookAuthor)
 | 
				
			||||||
 | 
					  BookAuthor.belongsTo(Book)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Person.hasMany(BookAuthor)
 | 
				
			||||||
 | 
					  BookAuthor.belongsTo(Person)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return BookAuthor
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										27
									
								
								server/models/BookChapter.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								server/models/BookChapter.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class BookChapter extends Model { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  BookChapter.init({
 | 
				
			||||||
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    index: DataTypes.INTEGER,
 | 
				
			||||||
 | 
					    title: DataTypes.STRING,
 | 
				
			||||||
 | 
					    start: DataTypes.INTEGER,
 | 
				
			||||||
 | 
					    end: DataTypes.INTEGER
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    modelName: 'BookChapter'
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const { Book } = sequelize.models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Book.hasMany(BookChapter)
 | 
				
			||||||
 | 
					  BookChapter.belongsTo(Book)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return BookChapter
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										31
									
								
								server/models/BookGenre.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								server/models/BookGenre.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class BookGenre extends Model { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  BookGenre.init({
 | 
				
			||||||
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    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 })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Book.hasMany(BookGenre)
 | 
				
			||||||
 | 
					  BookGenre.belongsTo(Book)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Genre.hasMany(BookGenre)
 | 
				
			||||||
 | 
					  BookGenre.belongsTo(Genre)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return BookGenre
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										31
									
								
								server/models/BookNarrator.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								server/models/BookNarrator.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class BookNarrator extends Model { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  BookNarrator.init({
 | 
				
			||||||
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    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 })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Book.hasMany(BookNarrator)
 | 
				
			||||||
 | 
					  BookNarrator.belongsTo(Book)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Person.hasMany(BookNarrator)
 | 
				
			||||||
 | 
					  BookNarrator.belongsTo(Person)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return BookNarrator
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										32
									
								
								server/models/BookSeries.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								server/models/BookSeries.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class BookSeries extends Model { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  BookSeries.init({
 | 
				
			||||||
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    sequence: DataTypes.STRING
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    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 })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Book.hasMany(BookSeries)
 | 
				
			||||||
 | 
					  BookSeries.belongsTo(Book)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Series.hasMany(BookSeries)
 | 
				
			||||||
 | 
					  BookSeries.belongsTo(Series)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return BookSeries
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										31
									
								
								server/models/BookTag.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								server/models/BookTag.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class BookTag extends Model { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  BookTag.init({
 | 
				
			||||||
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    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 })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Book.hasMany(BookTag)
 | 
				
			||||||
 | 
					  BookTag.belongsTo(Book)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Tag.hasMany(BookTag)
 | 
				
			||||||
 | 
					  BookTag.belongsTo(Tag)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return BookTag
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										25
									
								
								server/models/Collection.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								server/models/Collection.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class Collection extends Model { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Collection.init({
 | 
				
			||||||
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    name: DataTypes.STRING,
 | 
				
			||||||
 | 
					    description: DataTypes.TEXT
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    modelName: 'Collection'
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const { Library } = sequelize.models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Library.hasMany(Collection)
 | 
				
			||||||
 | 
					  Collection.belongsTo(Library)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return Collection
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								server/models/CollectionBook.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								server/models/CollectionBook.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class CollectionBook extends Model { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  CollectionBook.init({
 | 
				
			||||||
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    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 })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Book.hasMany(CollectionBook)
 | 
				
			||||||
 | 
					  CollectionBook.belongsTo(Book)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Collection.hasMany(CollectionBook)
 | 
				
			||||||
 | 
					  CollectionBook.belongsTo(Collection)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return CollectionBook
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										23
									
								
								server/models/EBookFile.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								server/models/EBookFile.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class EBookFile extends Model { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  EBookFile.init({
 | 
				
			||||||
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    modelName: 'EBookFile'
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const { FileMetadata } = sequelize.models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  FileMetadata.hasOne(EBookFile)
 | 
				
			||||||
 | 
					  EBookFile.belongsTo(FileMetadata)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return EBookFile
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										26
									
								
								server/models/FileMetadata.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								server/models/FileMetadata.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class FileMetadata extends Model { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  FileMetadata.init({
 | 
				
			||||||
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    ino: DataTypes.STRING,
 | 
				
			||||||
 | 
					    filename: DataTypes.STRING,
 | 
				
			||||||
 | 
					    ext: DataTypes.STRING,
 | 
				
			||||||
 | 
					    path: DataTypes.STRING,
 | 
				
			||||||
 | 
					    size: DataTypes.BIGINT,
 | 
				
			||||||
 | 
					    mtime: DataTypes.DATE(6),
 | 
				
			||||||
 | 
					    ctime: DataTypes.DATE(6),
 | 
				
			||||||
 | 
					    birthtime: DataTypes.DATE(6)
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    modelName: 'FileMetadata'
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return FileMetadata
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										19
									
								
								server/models/Genre.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								server/models/Genre.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class Genre extends Model { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Genre.init({
 | 
				
			||||||
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    name: DataTypes.STRING
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    modelName: 'Genre'
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return Genre
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										25
									
								
								server/models/Library.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								server/models/Library.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class Library extends Model { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Library.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,
 | 
				
			||||||
 | 
					    scanVersion: DataTypes.STRING
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    modelName: 'Library'
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return Library
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										25
									
								
								server/models/LibraryFile.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								server/models/LibraryFile.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class LibraryFile extends Model { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  LibraryFile.init({
 | 
				
			||||||
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    modelName: 'LibraryFile'
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const { LibraryItem, FileMetadata } = sequelize.models
 | 
				
			||||||
 | 
					  LibraryItem.hasMany(LibraryFile)
 | 
				
			||||||
 | 
					  LibraryFile.belongsTo(LibraryItem)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  FileMetadata.hasOne(LibraryFile)
 | 
				
			||||||
 | 
					  LibraryFile.belongsTo(FileMetadata)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return LibraryFile
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										23
									
								
								server/models/LibraryFolder.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								server/models/LibraryFolder.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class LibraryFolder extends Model { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  LibraryFolder.init({
 | 
				
			||||||
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    path: DataTypes.STRING
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    modelName: 'LibraryFolder'
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const { Library } = sequelize.models
 | 
				
			||||||
 | 
					  Library.hasMany(LibraryFolder)
 | 
				
			||||||
 | 
					  LibraryFolder.belongsTo(Library)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return LibraryFolder
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										34
									
								
								server/models/LibraryItem.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								server/models/LibraryItem.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class LibraryItem extends Model { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  LibraryItem.init({
 | 
				
			||||||
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    ino: DataTypes.STRING,
 | 
				
			||||||
 | 
					    path: DataTypes.STRING,
 | 
				
			||||||
 | 
					    relPath: DataTypes.STRING,
 | 
				
			||||||
 | 
					    mediaType: DataTypes.STRING,
 | 
				
			||||||
 | 
					    isFile: DataTypes.BOOLEAN,
 | 
				
			||||||
 | 
					    isMissing: DataTypes.BOOLEAN,
 | 
				
			||||||
 | 
					    isInvalid: DataTypes.BOOLEAN,
 | 
				
			||||||
 | 
					    mtime: DataTypes.DATE(6),
 | 
				
			||||||
 | 
					    ctime: DataTypes.DATE(6),
 | 
				
			||||||
 | 
					    birthtime: DataTypes.DATE(6),
 | 
				
			||||||
 | 
					    lastScan: DataTypes.DATE,
 | 
				
			||||||
 | 
					    scanVersion: DataTypes.STRING
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    modelName: 'LibraryItem'
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const { LibraryFolder } = sequelize.models
 | 
				
			||||||
 | 
					  LibraryFolder.hasMany(LibraryItem)
 | 
				
			||||||
 | 
					  LibraryItem.belongsTo(LibraryFolder)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return LibraryItem
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										75
									
								
								server/models/MediaProgress.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								server/models/MediaProgress.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,75 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const uppercaseFirst = str => `${str[0].toUpperCase()}${str.substr(1)}`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Polymorphic association: https://sequelize.org/docs/v6/advanced-association-concepts/polymorphic-associations/
 | 
				
			||||||
 | 
					 * Book has many MediaProgress. PodcastEpisode has many MediaProgress.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class MediaProgress extends Model {
 | 
				
			||||||
 | 
					    getMediaItem(options) {
 | 
				
			||||||
 | 
					      if (!this.mediaItemType) return Promise.resolve(null)
 | 
				
			||||||
 | 
					      const mixinMethodName = `get${uppercaseFirst(this.mediaItemType)}`
 | 
				
			||||||
 | 
					      return this[mixinMethodName](options)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  MediaProgress.init({
 | 
				
			||||||
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    mediaItemId: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					    mediaItemType: DataTypes.STRING,
 | 
				
			||||||
 | 
					    duration: DataTypes.INTEGER,
 | 
				
			||||||
 | 
					    currentTime: DataTypes.INTEGER,
 | 
				
			||||||
 | 
					    isFinished: DataTypes.BOOLEAN,
 | 
				
			||||||
 | 
					    hideFromContinueListening: DataTypes.BOOLEAN,
 | 
				
			||||||
 | 
					    finishedAt: DataTypes.DATE
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    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 })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  PodcastEpisode.hasMany(MediaProgress, {
 | 
				
			||||||
 | 
					    foreignKey: 'mediaItemId',
 | 
				
			||||||
 | 
					    constraints: false,
 | 
				
			||||||
 | 
					    scope: {
 | 
				
			||||||
 | 
					      mediaItemType: 'podcastEpisode'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  MediaProgress.belongsTo(PodcastEpisode, { foreignKey: 'mediaItemId', constraints: false })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  MediaProgress.addHook('afterFind', findResult => {
 | 
				
			||||||
 | 
					    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
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      // To prevent mistakes:
 | 
				
			||||||
 | 
					      delete instance.book
 | 
				
			||||||
 | 
					      delete instance.dataValues.book
 | 
				
			||||||
 | 
					      delete instance.podcastEpisode
 | 
				
			||||||
 | 
					      delete instance.dataValues.podcastEpisode
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  User.hasMany(MediaProgress)
 | 
				
			||||||
 | 
					  MediaProgress.belongsTo(User)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return MediaProgress
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										26
									
								
								server/models/Person.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								server/models/Person.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class Person extends Model { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Person.init({
 | 
				
			||||||
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    type: DataTypes.STRING,
 | 
				
			||||||
 | 
					    name: DataTypes.STRING,
 | 
				
			||||||
 | 
					    asin: DataTypes.STRING,
 | 
				
			||||||
 | 
					    description: DataTypes.TEXT
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    modelName: 'Person'
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const { FileMetadata } = sequelize.models
 | 
				
			||||||
 | 
					  FileMetadata.hasMany(Person)
 | 
				
			||||||
 | 
					  Person.belongsTo(FileMetadata, { as: 'ImageFile' }) // Ref: https://sequelize.org/docs/v6/core-concepts/assocs/#defining-an-alias
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return Person
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										46
									
								
								server/models/Podcast.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								server/models/Podcast.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class Podcast extends Model { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Podcast.init({
 | 
				
			||||||
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    // Metadata
 | 
				
			||||||
 | 
					    title: 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,
 | 
				
			||||||
 | 
					    type: DataTypes.STRING,
 | 
				
			||||||
 | 
					    explicit: DataTypes.BOOLEAN,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    autoDownloadEpisodes: DataTypes.BOOLEAN,
 | 
				
			||||||
 | 
					    autoDownloadSchedule: DataTypes.STRING,
 | 
				
			||||||
 | 
					    lastEpisodeCheck: DataTypes.DATE,
 | 
				
			||||||
 | 
					    maxEpisodesToKeep: DataTypes.INTEGER,
 | 
				
			||||||
 | 
					    maxNewEpisodesToDownload: DataTypes.INTEGER,
 | 
				
			||||||
 | 
					    lastCoverSearchQuery: DataTypes.STRING,
 | 
				
			||||||
 | 
					    lastCoverSearch: DataTypes.DATE
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    modelName: 'Podcast'
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const { LibraryItem, FileMetadata } = sequelize.models
 | 
				
			||||||
 | 
					  LibraryItem.hasOne(Podcast)
 | 
				
			||||||
 | 
					  Podcast.belongsTo(LibraryItem)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  FileMetadata.hasOne(Podcast)
 | 
				
			||||||
 | 
					  Podcast.belongsTo(FileMetadata, { as: 'ImageFile' }) // Ref: https://sequelize.org/docs/v6/core-concepts/assocs/#defining-an-alias
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return Podcast
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										34
									
								
								server/models/PodcastEpisode.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								server/models/PodcastEpisode.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class PodcastEpisode extends Model { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  PodcastEpisode.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,
 | 
				
			||||||
 | 
					    enclosureLength: DataTypes.BIGINT,
 | 
				
			||||||
 | 
					    enclosureType: DataTypes.STRING,
 | 
				
			||||||
 | 
					    publishedAt: DataTypes.DATE
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    modelName: 'PodcastEpisode'
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const { Podcast } = sequelize.models
 | 
				
			||||||
 | 
					  Podcast.hasMany(PodcastEpisode)
 | 
				
			||||||
 | 
					  PodcastEpisode.belongsTo(Podcast)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return PodcastEpisode
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										31
									
								
								server/models/PodcastTag.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								server/models/PodcastTag.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class PodcastTag extends Model { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  PodcastTag.init({
 | 
				
			||||||
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    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 })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Podcast.hasMany(PodcastTag)
 | 
				
			||||||
 | 
					  PodcastTag.belongsTo(Podcast)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Tag.hasMany(PodcastTag)
 | 
				
			||||||
 | 
					  PodcastTag.belongsTo(Tag)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return PodcastTag
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										20
									
								
								server/models/Series.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								server/models/Series.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class Series extends Model { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Series.init({
 | 
				
			||||||
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    name: DataTypes.STRING,
 | 
				
			||||||
 | 
					    description: DataTypes.TEXT
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    modelName: 'Series'
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return Series
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										19
									
								
								server/models/Tag.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								server/models/Tag.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					const { DataTypes, Model } = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = (sequelize) => {
 | 
				
			||||||
 | 
					  class Tag extends Model { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Tag.init({
 | 
				
			||||||
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    name: DataTypes.STRING
 | 
				
			||||||
 | 
					  }, {
 | 
				
			||||||
 | 
					    sequelize,
 | 
				
			||||||
 | 
					    modelName: 'Tag'
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return Tag
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -4,7 +4,18 @@ module.exports = (sequelize) => {
 | 
				
			|||||||
  class User extends Model { }
 | 
					  class User extends Model { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  User.init({
 | 
					  User.init({
 | 
				
			||||||
    username: DataTypes.STRING
 | 
					    id: {
 | 
				
			||||||
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
 | 
					      defaultValue: DataTypes.UUIDV4,
 | 
				
			||||||
 | 
					      primaryKey: true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    username: DataTypes.STRING,
 | 
				
			||||||
 | 
					    pash: DataTypes.STRING,
 | 
				
			||||||
 | 
					    type: DataTypes.STRING,
 | 
				
			||||||
 | 
					    token: DataTypes.STRING,
 | 
				
			||||||
 | 
					    isActive: DataTypes.BOOLEAN,
 | 
				
			||||||
 | 
					    isLocked: DataTypes.BOOLEAN,
 | 
				
			||||||
 | 
					    lastSeen: DataTypes.DATE
 | 
				
			||||||
  }, {
 | 
					  }, {
 | 
				
			||||||
    sequelize,
 | 
					    sequelize,
 | 
				
			||||||
    modelName: 'User'
 | 
					    modelName: 'User'
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user