mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-04-07 01:15:44 +02: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