diff --git a/server/Database.js b/server/Database.js index be81f761..e1963d8d 100644 --- a/server/Database.js +++ b/server/Database.js @@ -66,6 +66,11 @@ class Database { require('./models/PlaylistMediaItem')(this.sequelize) require('./models/Device')(this.sequelize) require('./models/PlaybackSession')(this.sequelize) + require('./models/PlaybackSessionListenTime')(this.sequelize) + require('./models/Feed')(this.sequelize) + require('./models/FeedEpisode')(this.sequelize) + require('./models/Setting')(this.sequelize) + require('./models/LibrarySetting')(this.sequelize) return this.sequelize.sync() } diff --git a/server/models/AudioTrack.js b/server/models/AudioTrack.js index 9dbf05fa..3fde96d0 100644 --- a/server/models/AudioTrack.js +++ b/server/models/AudioTrack.js @@ -30,6 +30,10 @@ module.exports = (sequelize) => { }) const { Book, PodcastEpisode, FileMetadata } = sequelize.models + + FileMetadata.hasOne(AudioTrack) + AudioTrack.belongsTo(FileMetadata) + Book.hasMany(AudioTrack, { foreignKey: 'mediaItemId', constraints: false, @@ -64,8 +68,5 @@ module.exports = (sequelize) => { } }) - FileMetadata.hasOne(AudioTrack) - AudioTrack.belongsTo(FileMetadata) - return AudioTrack } \ No newline at end of file diff --git a/server/models/Feed.js b/server/models/Feed.js new file mode 100644 index 00000000..30ba6e32 --- /dev/null +++ b/server/models/Feed.js @@ -0,0 +1,113 @@ +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/ + * Feeds can be created from LibraryItem, Collection, Playlist or Series + */ +module.exports = (sequelize) => { + class Feed extends Model { + getEntity(options) { + if (!this.entityType) return Promise.resolve(null) + const mixinMethodName = `get${uppercaseFirst(this.entityType)}` + return this[mixinMethodName](options) + } + } + + Feed.init({ + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + slug: DataTypes.STRING, + entityType: DataTypes.STRING, + entityId: DataTypes.UUIDV4, + entityUpdatedAt: DataTypes.DATE, + serverAddress: DataTypes.STRING, + feedURL: DataTypes.STRING, + imageURL: DataTypes.STRING, + siteURL: DataTypes.STRING, + title: DataTypes.STRING, + description: DataTypes.TEXT, + author: DataTypes.STRING, + podcastType: DataTypes.STRING, + language: DataTypes.STRING, + ownerName: DataTypes.STRING, + ownerEmail: DataTypes.STRING, + explicit: DataTypes.BOOLEAN, + preventIndexing: DataTypes.BOOLEAN + }, { + sequelize, + modelName: 'Feed' + }) + + const { User, LibraryItem, Collection, Series, Playlist } = sequelize.models + + User.hasMany(Feed) + Feed.belongsTo(User) + + LibraryItem.hasMany(Feed, { + foreignKey: 'entityId', + constraints: false, + scope: { + entityType: 'libraryItem' + } + }) + Feed.belongsTo(LibraryItem, { foreignKey: 'entityId', constraints: false }) + + Collection.hasMany(Feed, { + foreignKey: 'entityId', + constraints: false, + scope: { + entityType: 'collection' + } + }) + Feed.belongsTo(Collection, { foreignKey: 'entityId', constraints: false }) + + Series.hasMany(Feed, { + foreignKey: 'entityId', + constraints: false, + scope: { + entityType: 'series' + } + }) + Feed.belongsTo(Series, { foreignKey: 'entityId', constraints: false }) + + Playlist.hasMany(Feed, { + foreignKey: 'entityId', + constraints: false, + scope: { + entityType: 'playlist' + } + }) + Feed.belongsTo(Playlist, { foreignKey: 'entityId', constraints: false }) + + Feed.addHook('afterFind', findResult => { + if (!Array.isArray(findResult)) findResult = [findResult] + for (const instance of findResult) { + if (instance.entityType === 'libraryItem' && instance.LibraryItem !== undefined) { + instance.Entity = instance.LibraryItem + } else if (instance.mediaItemType === 'collection' && instance.Collection !== undefined) { + instance.Entity = instance.Collection + } else if (instance.mediaItemType === 'series' && instance.Series !== undefined) { + instance.Entity = instance.Series + } else if (instance.mediaItemType === 'playlist' && instance.Playlist !== undefined) { + instance.Entity = instance.Playlist + } + + // To prevent mistakes: + delete instance.LibraryItem + delete instance.dataValues.LibraryItem + delete instance.Collection + delete instance.dataValues.Collection + delete instance.Series + delete instance.dataValues.Series + delete instance.Playlist + delete instance.dataValues.Playlist + } + }) + + return Feed +} \ No newline at end of file diff --git a/server/models/FeedEpisode.js b/server/models/FeedEpisode.js new file mode 100644 index 00000000..ed99a539 --- /dev/null +++ b/server/models/FeedEpisode.js @@ -0,0 +1,37 @@ +const { DataTypes, Model } = require('sequelize') + +module.exports = (sequelize) => { + class FeedEpisode extends Model { } + + FeedEpisode.init({ + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + title: DataTypes.STRING, + author: DataTypes.STRING, + description: DataTypes.TEXT, + siteURL: DataTypes.STRING, + enclosureURL: DataTypes.STRING, + enclosureType: DataTypes.STRING, + enclosureSize: DataTypes.BIGINT, + pubDate: DataTypes.STRING, + season: DataTypes.STRING, + episode: DataTypes.STRING, + episodeType: DataTypes.STRING, + duration: DataTypes.INTEGER, + filePath: DataTypes.STRING, + explicit: DataTypes.BOOLEAN + }, { + sequelize, + modelName: 'FeedEpisode' + }) + + const { Feed } = sequelize.models + + Feed.hasMany(FeedEpisode) + FeedEpisode.belongsTo(Feed) + + return FeedEpisode +} \ No newline at end of file diff --git a/server/models/FileMetadata.js b/server/models/FileMetadata.js index 84dc5a1b..31c84797 100644 --- a/server/models/FileMetadata.js +++ b/server/models/FileMetadata.js @@ -19,6 +19,7 @@ module.exports = (sequelize) => { birthtime: DataTypes.DATE(6) }, { sequelize, + freezeTableName: true, // sequelize uses datum as singular of data modelName: 'FileMetadata' }) diff --git a/server/models/LibrarySetting.js b/server/models/LibrarySetting.js new file mode 100644 index 00000000..3b5243fd --- /dev/null +++ b/server/models/LibrarySetting.js @@ -0,0 +1,25 @@ +const { DataTypes, Model } = require('sequelize') + +module.exports = (sequelize) => { + class LibrarySetting extends Model { } + + LibrarySetting.init({ + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + key: DataTypes.STRING, + value: DataTypes.STRING + }, { + sequelize, + modelName: 'LibrarySetting' + }) + + const { Library } = sequelize.models + + Library.hasMany(LibrarySetting) + LibrarySetting.belongsTo(Library) + + return LibrarySetting +} \ No newline at end of file diff --git a/server/models/PlaybackSession.js b/server/models/PlaybackSession.js index 2697c050..9e13bad7 100644 --- a/server/models/PlaybackSession.js +++ b/server/models/PlaybackSession.js @@ -35,6 +35,12 @@ module.exports = (sequelize) => { const { Book, PodcastEpisode, User, Device } = sequelize.models + User.hasMany(PlaybackSession) + PlaybackSession.belongsTo(User) + + Device.hasMany(PlaybackSession) + PlaybackSession.belongsTo(Device) + Book.hasMany(PlaybackSession, { foreignKey: 'mediaItemId', constraints: false, @@ -69,11 +75,5 @@ module.exports = (sequelize) => { } }) - User.hasMany(PlaybackSession) - PlaybackSession.belongsTo(User) - - Device.hasMany(PlaybackSession) - PlaybackSession.belongsTo(Device) - return PlaybackSession } \ No newline at end of file diff --git a/server/models/PlaybackSessionListenTime.js b/server/models/PlaybackSessionListenTime.js new file mode 100644 index 00000000..a3f581ad --- /dev/null +++ b/server/models/PlaybackSessionListenTime.js @@ -0,0 +1,25 @@ +const { DataTypes, Model } = require('sequelize') + +module.exports = (sequelize) => { + class PlaybackSessionListenTime extends Model { } + + PlaybackSessionListenTime.init({ + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + time: DataTypes.INTEGER, + date: DataTypes.STRING + }, { + sequelize, + modelName: 'PlaybackSessionListenTime' + }) + + const { PlaybackSession } = sequelize.models + + PlaybackSession.hasMany(PlaybackSessionListenTime) + PlaybackSessionListenTime.belongsTo(PlaybackSession) + + return PlaybackSessionListenTime +} \ No newline at end of file diff --git a/server/models/PodcastEpisode.js b/server/models/PodcastEpisode.js index 73693c8a..355fca7e 100644 --- a/server/models/PodcastEpisode.js +++ b/server/models/PodcastEpisode.js @@ -18,7 +18,7 @@ module.exports = (sequelize) => { description: DataTypes.TEXT, pubDate: DataTypes.STRING, enclosureURL: DataTypes.STRING, - enclosureLength: DataTypes.BIGINT, + enclosureSize: DataTypes.BIGINT, enclosureType: DataTypes.STRING, publishedAt: DataTypes.DATE }, { diff --git a/server/models/Setting.js b/server/models/Setting.js new file mode 100644 index 00000000..53767f58 --- /dev/null +++ b/server/models/Setting.js @@ -0,0 +1,19 @@ +const { DataTypes, Model } = require('sequelize') + +module.exports = (sequelize) => { + class Setting extends Model { } + + Setting.init({ + key: { + type: DataTypes.STRING, + primaryKey: true + }, + value: DataTypes.STRING, + type: DataTypes.INTEGER + }, { + sequelize, + modelName: 'Setting' + }) + + return Setting +} \ No newline at end of file