Complete migration file

This commit is contained in:
advplyr 2023-03-18 16:56:57 -05:00
parent b8de041497
commit 243bc7b0d0
22 changed files with 1203 additions and 162 deletions

View File

@ -61,6 +61,7 @@ class Database {
require('./models/BookChapter')(this.sequelize) require('./models/BookChapter')(this.sequelize)
require('./models/Genre')(this.sequelize) require('./models/Genre')(this.sequelize)
require('./models/BookGenre')(this.sequelize) require('./models/BookGenre')(this.sequelize)
require('./models/PodcastGenre')(this.sequelize)
require('./models/BookNarrator')(this.sequelize) require('./models/BookNarrator')(this.sequelize)
require('./models/Series')(this.sequelize) require('./models/Series')(this.sequelize)
require('./models/BookSeries')(this.sequelize) require('./models/BookSeries')(this.sequelize)

View File

@ -1,7 +1,5 @@
const { DataTypes, Model } = require('sequelize') 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/ * Polymorphic association: https://sequelize.org/docs/v6/advanced-association-concepts/polymorphic-associations/
* Book has many AudioBookmark. PodcastEpisode has many AudioBookmark. * Book has many AudioBookmark. PodcastEpisode has many AudioBookmark.
@ -10,7 +8,7 @@ module.exports = (sequelize) => {
class AudioBookmark extends Model { class AudioBookmark extends Model {
getMediaItem(options) { getMediaItem(options) {
if (!this.mediaItemType) return Promise.resolve(null) if (!this.mediaItemType) return Promise.resolve(null)
const mixinMethodName = `get${uppercaseFirst(this.mediaItemType)}` const mixinMethodName = `get${this.mediaItemType}`
return this[mixinMethodName](options) return this[mixinMethodName](options)
} }
} }
@ -21,7 +19,7 @@ module.exports = (sequelize) => {
defaultValue: DataTypes.UUIDV4, defaultValue: DataTypes.UUIDV4,
primaryKey: true primaryKey: true
}, },
mediaItemId: DataTypes.UUIDV4, MediaItemId: DataTypes.UUIDV4,
mediaItemType: DataTypes.STRING, mediaItemType: DataTypes.STRING,
title: DataTypes.STRING, title: DataTypes.STRING,
time: DataTypes.INTEGER time: DataTypes.INTEGER
@ -32,29 +30,29 @@ module.exports = (sequelize) => {
const { User, Book, PodcastEpisode } = sequelize.models const { User, Book, PodcastEpisode } = sequelize.models
Book.hasMany(AudioBookmark, { Book.hasMany(AudioBookmark, {
foreignKey: 'mediaItemId', foreignKey: 'MediaItemId',
constraints: false, constraints: false,
scope: { scope: {
mediaItemType: 'book' mediaItemType: 'Book'
} }
}) })
AudioBookmark.belongsTo(Book, { foreignKey: 'mediaItemId', constraints: false }) AudioBookmark.belongsTo(Book, { foreignKey: 'MediaItemId', constraints: false })
PodcastEpisode.hasMany(AudioBookmark, { PodcastEpisode.hasMany(AudioBookmark, {
foreignKey: 'mediaItemId', foreignKey: 'MediaItemId',
constraints: false, constraints: false,
scope: { scope: {
mediaItemType: 'podcastEpisode' mediaItemType: 'PodcastEpisode'
} }
}) })
AudioBookmark.belongsTo(PodcastEpisode, { foreignKey: 'mediaItemId', constraints: false }) AudioBookmark.belongsTo(PodcastEpisode, { foreignKey: 'MediaItemId', constraints: false })
AudioBookmark.addHook('afterFind', findResult => { AudioBookmark.addHook('afterFind', findResult => {
if (!Array.isArray(findResult)) findResult = [findResult] if (!Array.isArray(findResult)) findResult = [findResult]
for (const instance of findResult) { for (const instance of findResult) {
if (instance.mediaItemType === 'book' && instance.Book !== undefined) { if (instance.mediaItemType === 'Book' && instance.Book !== undefined) {
instance.MediaItem = instance.Book instance.MediaItem = instance.Book
} else if (instance.mediaItemType === 'podcastEpisode' && instance.PodcastEpisode !== undefined) { } else if (instance.mediaItemType === 'PodcastEpisode' && instance.PodcastEpisode !== undefined) {
instance.MediaItem = instance.PodcastEpisode instance.MediaItem = instance.PodcastEpisode
} }
// To prevent mistakes: // To prevent mistakes:

View File

@ -1,7 +1,5 @@
const { DataTypes, Model } = require('sequelize') 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/ * Polymorphic association: https://sequelize.org/docs/v6/advanced-association-concepts/polymorphic-associations/
* Book has many AudioTrack. PodcastEpisode has one AudioTrack. * Book has many AudioTrack. PodcastEpisode has one AudioTrack.
@ -10,7 +8,7 @@ module.exports = (sequelize) => {
class AudioTrack extends Model { class AudioTrack extends Model {
getMediaItem(options) { getMediaItem(options) {
if (!this.mediaItemType) return Promise.resolve(null) if (!this.mediaItemType) return Promise.resolve(null)
const mixinMethodName = `get${uppercaseFirst(this.mediaItemType)}` const mixinMethodName = `get${this.mediaItemType}`
return this[mixinMethodName](options) return this[mixinMethodName](options)
} }
} }
@ -21,14 +19,16 @@ module.exports = (sequelize) => {
defaultValue: DataTypes.UUIDV4, defaultValue: DataTypes.UUIDV4,
primaryKey: true primaryKey: true
}, },
mediaItemId: DataTypes.UUIDV4, MediaItemId: DataTypes.UUIDV4,
mediaItemType: DataTypes.STRING, mediaItemType: DataTypes.STRING,
index: DataTypes.INTEGER, index: DataTypes.INTEGER,
startOffset: DataTypes.INTEGER, startOffset: DataTypes.FLOAT,
duration: DataTypes.INTEGER, duration: DataTypes.FLOAT,
title: DataTypes.STRING, title: DataTypes.STRING,
mimeType: DataTypes.STRING, mimeType: DataTypes.STRING,
codec: DataTypes.STRING codec: DataTypes.STRING,
trackNumber: DataTypes.INTEGER,
discNumber: DataTypes.INTEGER
}, { }, {
sequelize, sequelize,
modelName: 'AudioTrack' modelName: 'AudioTrack'
@ -40,29 +40,29 @@ module.exports = (sequelize) => {
AudioTrack.belongsTo(MediaFile) AudioTrack.belongsTo(MediaFile)
Book.hasMany(AudioTrack, { Book.hasMany(AudioTrack, {
foreignKey: 'mediaItemId', foreignKey: 'MediaItemId',
constraints: false, constraints: false,
scope: { scope: {
mediaItemType: 'book' mediaItemType: 'Book'
} }
}) })
AudioTrack.belongsTo(Book, { foreignKey: 'mediaItemId', constraints: false }) AudioTrack.belongsTo(Book, { foreignKey: 'MediaItemId', constraints: false })
PodcastEpisode.hasOne(AudioTrack, { PodcastEpisode.hasOne(AudioTrack, {
foreignKey: 'mediaItemId', foreignKey: 'MediaItemId',
constraints: false, constraints: false,
scope: { scope: {
mediaItemType: 'podcastEpisode' mediaItemType: 'PodcastEpisode'
} }
}) })
AudioTrack.belongsTo(PodcastEpisode, { foreignKey: 'mediaItemId', constraints: false }) AudioTrack.belongsTo(PodcastEpisode, { foreignKey: 'MediaItemId', constraints: false })
AudioTrack.addHook('afterFind', findResult => { AudioTrack.addHook('afterFind', findResult => {
if (!Array.isArray(findResult)) findResult = [findResult] if (!Array.isArray(findResult)) findResult = [findResult]
for (const instance of findResult) { for (const instance of findResult) {
if (instance.mediaItemType === 'book' && instance.Book !== undefined) { if (instance.mediaItemType === 'Book' && instance.Book !== undefined) {
instance.MediaItem = instance.Book instance.MediaItem = instance.Book
} else if (instance.mediaItemType === 'podcastEpisode' && instance.PodcastEpisode !== undefined) { } else if (instance.mediaItemType === 'PodcastEpisode' && instance.PodcastEpisode !== undefined) {
instance.MediaItem = instance.PodcastEpisode instance.MediaItem = instance.PodcastEpisode
} }
// To prevent mistakes: // To prevent mistakes:

View File

@ -30,8 +30,8 @@ module.exports = (sequelize) => {
LibraryItem.hasOne(Book) LibraryItem.hasOne(Book)
Book.belongsTo(LibraryItem) Book.belongsTo(LibraryItem)
FileMetadata.hasOne(Book) FileMetadata.hasOne(Book, { foreignKey: 'ImageFileId ' })
Book.belongsTo(FileMetadata, { as: 'ImageFile' }) // Ref: https://sequelize.org/docs/v6/core-concepts/assocs/#defining-an-alias Book.belongsTo(FileMetadata, { as: 'ImageFile', foreignKey: 'ImageFileId' }) // Ref: https://sequelize.org/docs/v6/core-concepts/assocs/#defining-an-alias
EBookFile.hasOne(Book) EBookFile.hasOne(Book)
Book.belongsTo(EBookFile) Book.belongsTo(EBookFile)

View File

@ -11,6 +11,8 @@ module.exports = (sequelize) => {
} }
}, { }, {
sequelize, sequelize,
timestamps: true,
updatedAt: false,
modelName: 'CollectionBook' modelName: 'CollectionBook'
}) })

View File

@ -16,8 +16,8 @@ module.exports = (sequelize) => {
const { FileMetadata } = sequelize.models const { FileMetadata } = sequelize.models
FileMetadata.hasOne(EBookFile) FileMetadata.hasOne(EBookFile, { foreignKey: 'FileMetadataId' })
EBookFile.belongsTo(FileMetadata) EBookFile.belongsTo(FileMetadata, { as: 'FileMetadata', foreignKey: 'FileMetadataId' })
return EBookFile return EBookFile
} }

View File

@ -1,7 +1,5 @@
const { DataTypes, Model } = require('sequelize') 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/ * Polymorphic association: https://sequelize.org/docs/v6/advanced-association-concepts/polymorphic-associations/
* Feeds can be created from LibraryItem, Collection, Playlist or Series * Feeds can be created from LibraryItem, Collection, Playlist or Series
@ -10,7 +8,7 @@ module.exports = (sequelize) => {
class Feed extends Model { class Feed extends Model {
getEntity(options) { getEntity(options) {
if (!this.entityType) return Promise.resolve(null) if (!this.entityType) return Promise.resolve(null)
const mixinMethodName = `get${uppercaseFirst(this.entityType)}` const mixinMethodName = `get${this.entityType}`
return this[mixinMethodName](options) return this[mixinMethodName](options)
} }
} }
@ -23,7 +21,7 @@ module.exports = (sequelize) => {
}, },
slug: DataTypes.STRING, slug: DataTypes.STRING,
entityType: DataTypes.STRING, entityType: DataTypes.STRING,
entityId: DataTypes.UUIDV4, EntityId: DataTypes.UUIDV4,
entityUpdatedAt: DataTypes.DATE, entityUpdatedAt: DataTypes.DATE,
serverAddress: DataTypes.STRING, serverAddress: DataTypes.STRING,
feedURL: DataTypes.STRING, feedURL: DataTypes.STRING,
@ -49,51 +47,51 @@ module.exports = (sequelize) => {
Feed.belongsTo(User) Feed.belongsTo(User)
LibraryItem.hasMany(Feed, { LibraryItem.hasMany(Feed, {
foreignKey: 'entityId', foreignKey: 'EntityId',
constraints: false, constraints: false,
scope: { scope: {
entityType: 'libraryItem' entityType: 'LibraryItem'
} }
}) })
Feed.belongsTo(LibraryItem, { foreignKey: 'entityId', constraints: false }) Feed.belongsTo(LibraryItem, { foreignKey: 'EntityId', constraints: false })
Collection.hasMany(Feed, { Collection.hasMany(Feed, {
foreignKey: 'entityId', foreignKey: 'EntityId',
constraints: false, constraints: false,
scope: { scope: {
entityType: 'collection' entityType: 'Collection'
} }
}) })
Feed.belongsTo(Collection, { foreignKey: 'entityId', constraints: false }) Feed.belongsTo(Collection, { foreignKey: 'EntityId', constraints: false })
Series.hasMany(Feed, { Series.hasMany(Feed, {
foreignKey: 'entityId', foreignKey: 'EntityId',
constraints: false, constraints: false,
scope: { scope: {
entityType: 'series' entityType: 'Series'
} }
}) })
Feed.belongsTo(Series, { foreignKey: 'entityId', constraints: false }) Feed.belongsTo(Series, { foreignKey: 'EntityId', constraints: false })
Playlist.hasMany(Feed, { Playlist.hasMany(Feed, {
foreignKey: 'entityId', foreignKey: 'EntityId',
constraints: false, constraints: false,
scope: { scope: {
entityType: 'playlist' entityType: 'Playlist'
} }
}) })
Feed.belongsTo(Playlist, { foreignKey: 'entityId', constraints: false }) Feed.belongsTo(Playlist, { foreignKey: 'EntityId', constraints: false })
Feed.addHook('afterFind', findResult => { Feed.addHook('afterFind', findResult => {
if (!Array.isArray(findResult)) findResult = [findResult] if (!Array.isArray(findResult)) findResult = [findResult]
for (const instance of findResult) { for (const instance of findResult) {
if (instance.entityType === 'libraryItem' && instance.LibraryItem !== undefined) { if (instance.entityType === 'LibraryItem' && instance.LibraryItem !== undefined) {
instance.Entity = instance.LibraryItem instance.Entity = instance.LibraryItem
} else if (instance.mediaItemType === 'collection' && instance.Collection !== undefined) { } else if (instance.mediaItemType === 'Collection' && instance.Collection !== undefined) {
instance.Entity = instance.Collection instance.Entity = instance.Collection
} else if (instance.mediaItemType === 'series' && instance.Series !== undefined) { } else if (instance.mediaItemType === 'Series' && instance.Series !== undefined) {
instance.Entity = instance.Series instance.Entity = instance.Series
} else if (instance.mediaItemType === 'playlist' && instance.Playlist !== undefined) { } else if (instance.mediaItemType === 'Playlist' && instance.Playlist !== undefined) {
instance.Entity = instance.Playlist instance.Entity = instance.Playlist
} }

View File

@ -20,7 +20,7 @@ module.exports = (sequelize) => {
season: DataTypes.STRING, season: DataTypes.STRING,
episode: DataTypes.STRING, episode: DataTypes.STRING,
episodeType: DataTypes.STRING, episodeType: DataTypes.STRING,
duration: DataTypes.INTEGER, duration: DataTypes.FLOAT,
filePath: DataTypes.STRING, filePath: DataTypes.STRING,
explicit: DataTypes.BOOLEAN explicit: DataTypes.BOOLEAN
}, { }, {

View File

@ -20,6 +20,10 @@ module.exports = (sequelize) => {
}, { }, {
sequelize, sequelize,
freezeTableName: true, // sequelize uses datum as singular of data freezeTableName: true, // sequelize uses datum as singular of data
name: {
singular: 'FileMetadata',
plural: 'FileMetadata'
},
modelName: 'FileMetadata' modelName: 'FileMetadata'
}) })

View File

@ -9,7 +9,8 @@ module.exports = (sequelize) => {
defaultValue: DataTypes.UUIDV4, defaultValue: DataTypes.UUIDV4,
primaryKey: true primaryKey: true
}, },
name: DataTypes.STRING name: DataTypes.STRING,
cleanName: DataTypes.STRING
}, { }, {
sequelize, sequelize,
modelName: 'Genre' modelName: 'Genre'

View File

@ -18,8 +18,8 @@ module.exports = (sequelize) => {
LibraryItem.hasMany(LibraryFile) LibraryItem.hasMany(LibraryFile)
LibraryFile.belongsTo(LibraryItem) LibraryFile.belongsTo(LibraryItem)
FileMetadata.hasOne(LibraryFile) FileMetadata.hasOne(LibraryFile, { foreignKey: 'FileMetadataId' })
LibraryFile.belongsTo(FileMetadata) LibraryFile.belongsTo(FileMetadata, { as: 'FileMetadata', foreignKey: 'FileMetadataId' })
return LibraryFile return LibraryFile
} }

View File

@ -22,8 +22,8 @@ module.exports = (sequelize) => {
const { FileMetadata } = sequelize.models const { FileMetadata } = sequelize.models
FileMetadata.hasOne(MediaFile) FileMetadata.hasOne(MediaFile, { foreignKey: 'FileMetadataId' })
MediaFile.belongsTo(FileMetadata) MediaFile.belongsTo(FileMetadata, { as: 'FileMetadata', foreignKey: 'FileMetadataId' })
return MediaFile return MediaFile
} }

View File

@ -1,7 +1,5 @@
const { DataTypes, Model } = require('sequelize') 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/ * Polymorphic association: https://sequelize.org/docs/v6/advanced-association-concepts/polymorphic-associations/
* Book has many MediaProgress. PodcastEpisode has many MediaProgress. * Book has many MediaProgress. PodcastEpisode has many MediaProgress.
@ -10,7 +8,7 @@ module.exports = (sequelize) => {
class MediaProgress extends Model { class MediaProgress extends Model {
getMediaItem(options) { getMediaItem(options) {
if (!this.mediaItemType) return Promise.resolve(null) if (!this.mediaItemType) return Promise.resolve(null)
const mixinMethodName = `get${uppercaseFirst(this.mediaItemType)}` const mixinMethodName = `get${this.mediaItemType}`
return this[mixinMethodName](options) return this[mixinMethodName](options)
} }
} }
@ -21,10 +19,10 @@ module.exports = (sequelize) => {
defaultValue: DataTypes.UUIDV4, defaultValue: DataTypes.UUIDV4,
primaryKey: true primaryKey: true
}, },
mediaItemId: DataTypes.UUIDV4, MediaItemId: DataTypes.UUIDV4,
mediaItemType: DataTypes.STRING, mediaItemType: DataTypes.STRING,
duration: DataTypes.INTEGER, duration: DataTypes.FLOAT,
currentTime: DataTypes.INTEGER, currentTime: DataTypes.FLOAT,
isFinished: DataTypes.BOOLEAN, isFinished: DataTypes.BOOLEAN,
hideFromContinueListening: DataTypes.BOOLEAN, hideFromContinueListening: DataTypes.BOOLEAN,
finishedAt: DataTypes.DATE finishedAt: DataTypes.DATE
@ -35,29 +33,29 @@ module.exports = (sequelize) => {
const { Book, PodcastEpisode, User } = sequelize.models const { Book, PodcastEpisode, User } = sequelize.models
Book.hasMany(MediaProgress, { Book.hasMany(MediaProgress, {
foreignKey: 'mediaItemId', foreignKey: 'MediaItemId',
constraints: false, constraints: false,
scope: { scope: {
mediaItemType: 'book' mediaItemType: 'Book'
} }
}) })
MediaProgress.belongsTo(Book, { foreignKey: 'mediaItemId', constraints: false }) MediaProgress.belongsTo(Book, { foreignKey: 'MediaItemId', constraints: false })
PodcastEpisode.hasMany(MediaProgress, { PodcastEpisode.hasMany(MediaProgress, {
foreignKey: 'mediaItemId', foreignKey: 'MediaItemId',
constraints: false, constraints: false,
scope: { scope: {
mediaItemType: 'podcastEpisode' mediaItemType: 'PodcastEpisode'
} }
}) })
MediaProgress.belongsTo(PodcastEpisode, { foreignKey: 'mediaItemId', constraints: false }) MediaProgress.belongsTo(PodcastEpisode, { foreignKey: 'MediaItemId', constraints: false })
MediaProgress.addHook('afterFind', findResult => { MediaProgress.addHook('afterFind', findResult => {
if (!Array.isArray(findResult)) findResult = [findResult] if (!Array.isArray(findResult)) findResult = [findResult]
for (const instance of findResult) { for (const instance of findResult) {
if (instance.mediaItemType === 'book' && instance.Book !== undefined) { if (instance.mediaItemType === 'Book' && instance.Book !== undefined) {
instance.MediaItem = instance.Book instance.MediaItem = instance.Book
} else if (instance.mediaItemType === 'podcastEpisode' && instance.PodcastEpisode !== undefined) { } else if (instance.mediaItemType === 'PodcastEpisode' && instance.PodcastEpisode !== undefined) {
instance.MediaItem = instance.PodcastEpisode instance.MediaItem = instance.PodcastEpisode
} }
// To prevent mistakes: // To prevent mistakes:

View File

@ -10,7 +10,7 @@ module.exports = (sequelize) => {
primaryKey: true primaryKey: true
}, },
eventName: DataTypes.STRING, eventName: DataTypes.STRING,
urls: DataTypes.TEXT, // JSON array of urls urls: DataTypes.JSON, // JSON array of urls
titleTemplate: DataTypes.STRING(1000), titleTemplate: DataTypes.STRING(1000),
bodyTemplate: DataTypes.TEXT, bodyTemplate: DataTypes.TEXT,
type: DataTypes.STRING, type: DataTypes.STRING,
@ -18,16 +18,12 @@ module.exports = (sequelize) => {
lastAttemptFailed: DataTypes.BOOLEAN, lastAttemptFailed: DataTypes.BOOLEAN,
numConsecutiveFailedAttempts: DataTypes.INTEGER, numConsecutiveFailedAttempts: DataTypes.INTEGER,
numTimesFired: DataTypes.INTEGER, numTimesFired: DataTypes.INTEGER,
enabled: DataTypes.BOOLEAN enabled: DataTypes.BOOLEAN,
extraData: DataTypes.JSON
}, { }, {
sequelize, sequelize,
modelName: 'Notification' modelName: 'Notification'
}) })
const { Library } = sequelize.models
Library.hasMany(Notification)
Notification.belongsTo(Library)
return Notification return Notification
} }

View File

@ -19,8 +19,8 @@ module.exports = (sequelize) => {
}) })
const { FileMetadata } = sequelize.models const { FileMetadata } = sequelize.models
FileMetadata.hasMany(Person) FileMetadata.hasMany(Person, { foreignKey: 'ImageFileId' })
Person.belongsTo(FileMetadata, { as: 'ImageFile' }) // Ref: https://sequelize.org/docs/v6/core-concepts/assocs/#defining-an-alias Person.belongsTo(FileMetadata, { as: 'ImageFile', foreignKey: 'ImageFileId' }) // Ref: https://sequelize.org/docs/v6/core-concepts/assocs/#defining-an-alias
return Person return Person
} }

View File

@ -1,12 +1,10 @@
const { DataTypes, Model } = require('sequelize') const { DataTypes, Model } = require('sequelize')
const uppercaseFirst = str => `${str[0].toUpperCase()}${str.substr(1)}`
module.exports = (sequelize) => { module.exports = (sequelize) => {
class PlaybackSession extends Model { class PlaybackSession extends Model {
getMediaItem(options) { getMediaItem(options) {
if (!this.mediaItemType) return Promise.resolve(null) if (!this.mediaItemType) return Promise.resolve(null)
const mixinMethodName = `get${uppercaseFirst(this.mediaItemType)}` const mixinMethodName = `get${this.mediaItemType}`
return this[mixinMethodName](options) return this[mixinMethodName](options)
} }
} }
@ -17,16 +15,15 @@ module.exports = (sequelize) => {
defaultValue: DataTypes.UUIDV4, defaultValue: DataTypes.UUIDV4,
primaryKey: true primaryKey: true
}, },
mediaItemId: DataTypes.UUIDV4, MediaItemId: DataTypes.UUIDV4,
mediaItemType: DataTypes.STRING, mediaItemType: DataTypes.STRING,
displayTitle: DataTypes.STRING, displayTitle: DataTypes.STRING,
displayAuthor: DataTypes.STRING, displayAuthor: DataTypes.STRING,
duration: DataTypes.INTEGER, duration: DataTypes.FLOAT,
playMethod: DataTypes.STRING, playMethod: DataTypes.STRING,
mediaPlayer: DataTypes.STRING, mediaPlayer: DataTypes.STRING,
startTime: DataTypes.INTEGER, startTime: DataTypes.FLOAT,
currentTime: DataTypes.INTEGER, currentTime: DataTypes.FLOAT,
timeListening: DataTypes.INTEGER,
serverVersion: DataTypes.STRING serverVersion: DataTypes.STRING
}, { }, {
sequelize, sequelize,
@ -42,29 +39,29 @@ module.exports = (sequelize) => {
PlaybackSession.belongsTo(Device) PlaybackSession.belongsTo(Device)
Book.hasMany(PlaybackSession, { Book.hasMany(PlaybackSession, {
foreignKey: 'mediaItemId', foreignKey: 'MediaItemId',
constraints: false, constraints: false,
scope: { scope: {
mediaItemType: 'book' mediaItemType: 'Book'
} }
}) })
PlaybackSession.belongsTo(Book, { foreignKey: 'mediaItemId', constraints: false }) PlaybackSession.belongsTo(Book, { foreignKey: 'MediaItemId', constraints: false })
PodcastEpisode.hasOne(PlaybackSession, { PodcastEpisode.hasOne(PlaybackSession, {
foreignKey: 'mediaItemId', foreignKey: 'MediaItemId',
constraints: false, constraints: false,
scope: { scope: {
mediaItemType: 'podcastEpisode' mediaItemType: 'PodcastEpisode'
} }
}) })
PlaybackSession.belongsTo(PodcastEpisode, { foreignKey: 'mediaItemId', constraints: false }) PlaybackSession.belongsTo(PodcastEpisode, { foreignKey: 'MediaItemId', constraints: false })
PlaybackSession.addHook('afterFind', findResult => { PlaybackSession.addHook('afterFind', findResult => {
if (!Array.isArray(findResult)) findResult = [findResult] if (!Array.isArray(findResult)) findResult = [findResult]
for (const instance of findResult) { for (const instance of findResult) {
if (instance.mediaItemType === 'book' && instance.Book !== undefined) { if (instance.mediaItemType === 'Book' && instance.Book !== undefined) {
instance.MediaItem = instance.Book instance.MediaItem = instance.Book
} else if (instance.mediaItemType === 'podcastEpisode' && instance.PodcastEpisode !== undefined) { } else if (instance.mediaItemType === 'PodcastEpisode' && instance.PodcastEpisode !== undefined) {
instance.MediaItem = instance.PodcastEpisode instance.MediaItem = instance.PodcastEpisode
} }
// To prevent mistakes: // To prevent mistakes:

View File

@ -1,12 +1,10 @@
const { DataTypes, Model } = require('sequelize') const { DataTypes, Model } = require('sequelize')
const uppercaseFirst = str => `${str[0].toUpperCase()}${str.substr(1)}`
module.exports = (sequelize) => { module.exports = (sequelize) => {
class PlaylistMediaItem extends Model { class PlaylistMediaItem extends Model {
getMediaItem(options) { getMediaItem(options) {
if (!this.mediaItemType) return Promise.resolve(null) if (!this.mediaItemType) return Promise.resolve(null)
const mixinMethodName = `get${uppercaseFirst(this.mediaItemType)}` const mixinMethodName = `get${this.mediaItemType}`
return this[mixinMethodName](options) return this[mixinMethodName](options)
} }
} }
@ -17,39 +15,41 @@ module.exports = (sequelize) => {
defaultValue: DataTypes.UUIDV4, defaultValue: DataTypes.UUIDV4,
primaryKey: true primaryKey: true
}, },
mediaItemId: DataTypes.UUIDV4, MediaItemId: DataTypes.UUIDV4,
mediaItemType: DataTypes.STRING mediaItemType: DataTypes.STRING
}, { }, {
sequelize, sequelize,
timestamps: true,
updatedAt: false,
modelName: 'PlaylistMediaItem' modelName: 'PlaylistMediaItem'
}) })
const { Book, PodcastEpisode, Playlist } = sequelize.models const { Book, PodcastEpisode, Playlist } = sequelize.models
Book.hasMany(PlaylistMediaItem, { Book.hasMany(PlaylistMediaItem, {
foreignKey: 'mediaItemId', foreignKey: 'MediaItemId',
constraints: false, constraints: false,
scope: { scope: {
mediaItemType: 'book' mediaItemType: 'Book'
} }
}) })
PlaylistMediaItem.belongsTo(Book, { foreignKey: 'mediaItemId', constraints: false }) PlaylistMediaItem.belongsTo(Book, { foreignKey: 'MediaItemId', constraints: false })
PodcastEpisode.hasOne(PlaylistMediaItem, { PodcastEpisode.hasOne(PlaylistMediaItem, {
foreignKey: 'mediaItemId', foreignKey: 'MediaItemId',
constraints: false, constraints: false,
scope: { scope: {
mediaItemType: 'podcastEpisode' mediaItemType: 'PodcastEpisode'
} }
}) })
PlaylistMediaItem.belongsTo(PodcastEpisode, { foreignKey: 'mediaItemId', constraints: false }) PlaylistMediaItem.belongsTo(PodcastEpisode, { foreignKey: 'MediaItemId', constraints: false })
PlaylistMediaItem.addHook('afterFind', findResult => { PlaylistMediaItem.addHook('afterFind', findResult => {
if (!Array.isArray(findResult)) findResult = [findResult] if (!Array.isArray(findResult)) findResult = [findResult]
for (const instance of findResult) { for (const instance of findResult) {
if (instance.mediaItemType === 'book' && instance.Book !== undefined) { if (instance.mediaItemType === 'Book' && instance.Book !== undefined) {
instance.MediaItem = instance.Book instance.MediaItem = instance.Book
} else if (instance.mediaItemType === 'podcastEpisode' && instance.PodcastEpisode !== undefined) { } else if (instance.mediaItemType === 'PodcastEpisode' && instance.PodcastEpisode !== undefined) {
instance.MediaItem = instance.PodcastEpisode instance.MediaItem = instance.PodcastEpisode
} }
// To prevent mistakes: // To prevent mistakes:

View File

@ -13,14 +13,14 @@ module.exports = (sequelize) => {
title: DataTypes.STRING, title: DataTypes.STRING,
author: DataTypes.STRING, author: DataTypes.STRING,
releaseDate: DataTypes.STRING, releaseDate: DataTypes.STRING,
feedUrl: DataTypes.STRING, feedURL: DataTypes.STRING,
imageUrl: DataTypes.STRING, imageURL: DataTypes.STRING,
description: DataTypes.TEXT, description: DataTypes.TEXT,
itunesPageUrl: DataTypes.STRING, itunesPageURL: DataTypes.STRING,
itunesId: DataTypes.STRING, itunesId: DataTypes.STRING,
itunesArtistId: DataTypes.STRING, itunesArtistId: DataTypes.STRING,
language: DataTypes.STRING, language: DataTypes.STRING,
type: DataTypes.STRING, podcastType: DataTypes.STRING,
explicit: DataTypes.BOOLEAN, explicit: DataTypes.BOOLEAN,
autoDownloadEpisodes: DataTypes.BOOLEAN, autoDownloadEpisodes: DataTypes.BOOLEAN,
@ -39,8 +39,8 @@ module.exports = (sequelize) => {
LibraryItem.hasOne(Podcast) LibraryItem.hasOne(Podcast)
Podcast.belongsTo(LibraryItem) Podcast.belongsTo(LibraryItem)
FileMetadata.hasOne(Podcast) FileMetadata.hasOne(Podcast, { foreignKey: 'ImageFileId' })
Podcast.belongsTo(FileMetadata, { as: 'ImageFile' }) // Ref: https://sequelize.org/docs/v6/core-concepts/assocs/#defining-an-alias Podcast.belongsTo(FileMetadata, { as: 'ImageFile', foreignKey: 'ImageFileId' }) // Ref: https://sequelize.org/docs/v6/core-concepts/assocs/#defining-an-alias
return Podcast return Podcast
} }

View File

@ -0,0 +1,31 @@
const { DataTypes, Model } = require('sequelize')
module.exports = (sequelize) => {
class PodcastGenre extends Model { }
PodcastGenre.init({
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
}
}, {
sequelize,
modelName: 'PodcastGenre',
timestamps: false
})
// Super Many-to-Many
// ref: https://sequelize.org/docs/v6/advanced-association-concepts/advanced-many-to-many/#the-best-of-both-worlds-the-super-many-to-many-relationship
const { Podcast, Genre } = sequelize.models
Podcast.belongsToMany(Genre, { through: PodcastGenre })
Genre.belongsToMany(Podcast, { through: PodcastGenre })
Podcast.hasMany(PodcastGenre)
PodcastGenre.belongsTo(Podcast)
Genre.hasMany(PodcastGenre)
PodcastGenre.belongsTo(Genre)
return PodcastGenre
}

View File

@ -9,7 +9,8 @@ module.exports = (sequelize) => {
defaultValue: DataTypes.UUIDV4, defaultValue: DataTypes.UUIDV4,
primaryKey: true primaryKey: true
}, },
name: DataTypes.STRING name: DataTypes.STRING,
cleanName: DataTypes.STRING
}, { }, {
sequelize, sequelize,
modelName: 'Tag' modelName: 'Tag'

View File

@ -22,7 +22,8 @@ module.exports = (sequelize) => {
type: DataTypes.BOOLEAN, type: DataTypes.BOOLEAN,
defaultValue: false defaultValue: false
}, },
lastSeen: DataTypes.DATE lastSeen: DataTypes.DATE,
extraData: DataTypes.JSON
}, { }, {
sequelize, sequelize,
modelName: 'User' modelName: 'User'

File diff suppressed because it is too large Load Diff