mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-06-03 05:34:26 -04:00
Add jsdoc types to remaining models
This commit is contained in:
parent
0bc89cd40f
commit
a98942a361
@ -92,27 +92,27 @@ class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buildModels(force = false) {
|
buildModels(force = false) {
|
||||||
require('./models/User')(this.sequelize)
|
require('./models/User').init(this.sequelize)
|
||||||
require('./models/Library').init(this.sequelize)
|
require('./models/Library').init(this.sequelize)
|
||||||
require('./models/LibraryFolder').init(this.sequelize)
|
require('./models/LibraryFolder').init(this.sequelize)
|
||||||
require('./models/Book').init(this.sequelize)
|
require('./models/Book').init(this.sequelize)
|
||||||
require('./models/Podcast')(this.sequelize)
|
require('./models/Podcast').init(this.sequelize)
|
||||||
require('./models/PodcastEpisode')(this.sequelize)
|
require('./models/PodcastEpisode').init(this.sequelize)
|
||||||
require('./models/LibraryItem')(this.sequelize)
|
require('./models/LibraryItem').init(this.sequelize)
|
||||||
require('./models/MediaProgress')(this.sequelize)
|
require('./models/MediaProgress').init(this.sequelize)
|
||||||
require('./models/Series')(this.sequelize)
|
require('./models/Series').init(this.sequelize)
|
||||||
require('./models/BookSeries').init(this.sequelize)
|
require('./models/BookSeries').init(this.sequelize)
|
||||||
require('./models/Author').init(this.sequelize)
|
require('./models/Author').init(this.sequelize)
|
||||||
require('./models/BookAuthor').init(this.sequelize)
|
require('./models/BookAuthor').init(this.sequelize)
|
||||||
require('./models/Collection').init(this.sequelize)
|
require('./models/Collection').init(this.sequelize)
|
||||||
require('./models/CollectionBook').init(this.sequelize)
|
require('./models/CollectionBook').init(this.sequelize)
|
||||||
require('./models/Playlist')(this.sequelize)
|
require('./models/Playlist').init(this.sequelize)
|
||||||
require('./models/PlaylistMediaItem')(this.sequelize)
|
require('./models/PlaylistMediaItem').init(this.sequelize)
|
||||||
require('./models/Device').init(this.sequelize)
|
require('./models/Device').init(this.sequelize)
|
||||||
require('./models/PlaybackSession')(this.sequelize)
|
require('./models/PlaybackSession').init(this.sequelize)
|
||||||
require('./models/Feed').init(this.sequelize)
|
require('./models/Feed').init(this.sequelize)
|
||||||
require('./models/FeedEpisode').init(this.sequelize)
|
require('./models/FeedEpisode').init(this.sequelize)
|
||||||
require('./models/Setting')(this.sequelize)
|
require('./models/Setting').init(this.sequelize)
|
||||||
|
|
||||||
return this.sequelize.sync({ force, alter: false })
|
return this.sequelize.sync({ force, alter: false })
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,148 +1,184 @@
|
|||||||
const { DataTypes, Model } = require('sequelize')
|
const { DataTypes, Model } = require('sequelize')
|
||||||
|
|
||||||
/*
|
class MediaProgress extends Model {
|
||||||
* Polymorphic association: https://sequelize.org/docs/v6/advanced-association-concepts/polymorphic-associations/
|
constructor(values, options) {
|
||||||
* Book has many MediaProgress. PodcastEpisode has many MediaProgress.
|
super(values, options)
|
||||||
*/
|
|
||||||
module.exports = (sequelize) => {
|
|
||||||
class MediaProgress extends Model {
|
|
||||||
getOldMediaProgress() {
|
|
||||||
const isPodcastEpisode = this.mediaItemType === 'podcastEpisode'
|
|
||||||
|
|
||||||
return {
|
/** @type {UUIDV4} */
|
||||||
id: this.id,
|
this.id
|
||||||
userId: this.userId,
|
/** @type {UUIDV4} */
|
||||||
libraryItemId: this.extraData?.libraryItemId || null,
|
this.mediaItemId
|
||||||
episodeId: isPodcastEpisode ? this.mediaItemId : null,
|
/** @type {string} */
|
||||||
mediaItemId: this.mediaItemId,
|
this.mediaItemType
|
||||||
mediaItemType: this.mediaItemType,
|
/** @type {number} */
|
||||||
duration: this.duration,
|
this.duration
|
||||||
progress: this.extraData?.progress || 0,
|
/** @type {number} */
|
||||||
currentTime: this.currentTime,
|
this.currentTime
|
||||||
isFinished: !!this.isFinished,
|
/** @type {boolean} */
|
||||||
hideFromContinueListening: !!this.hideFromContinueListening,
|
this.isFinished
|
||||||
ebookLocation: this.ebookLocation,
|
/** @type {boolean} */
|
||||||
ebookProgress: this.ebookProgress,
|
this.hideFromContinueListening
|
||||||
lastUpdate: this.updatedAt.valueOf(),
|
/** @type {string} */
|
||||||
startedAt: this.createdAt.valueOf(),
|
this.ebookLocation
|
||||||
finishedAt: this.finishedAt?.valueOf() || null
|
/** @type {number} */
|
||||||
}
|
this.ebookProgress
|
||||||
}
|
/** @type {Date} */
|
||||||
|
this.finishedAt
|
||||||
|
/** @type {Object} */
|
||||||
|
this.extraData
|
||||||
|
/** @type {UUIDV4} */
|
||||||
|
this.userId
|
||||||
|
/** @type {Date} */
|
||||||
|
this.updatedAt
|
||||||
|
/** @type {Date} */
|
||||||
|
this.createdAt
|
||||||
|
}
|
||||||
|
|
||||||
static upsertFromOld(oldMediaProgress) {
|
getOldMediaProgress() {
|
||||||
const mediaProgress = this.getFromOld(oldMediaProgress)
|
const isPodcastEpisode = this.mediaItemType === 'podcastEpisode'
|
||||||
return this.upsert(mediaProgress)
|
|
||||||
}
|
|
||||||
|
|
||||||
static getFromOld(oldMediaProgress) {
|
return {
|
||||||
return {
|
id: this.id,
|
||||||
id: oldMediaProgress.id,
|
userId: this.userId,
|
||||||
userId: oldMediaProgress.userId,
|
libraryItemId: this.extraData?.libraryItemId || null,
|
||||||
mediaItemId: oldMediaProgress.mediaItemId,
|
episodeId: isPodcastEpisode ? this.mediaItemId : null,
|
||||||
mediaItemType: oldMediaProgress.mediaItemType,
|
mediaItemId: this.mediaItemId,
|
||||||
duration: oldMediaProgress.duration,
|
mediaItemType: this.mediaItemType,
|
||||||
currentTime: oldMediaProgress.currentTime,
|
duration: this.duration,
|
||||||
ebookLocation: oldMediaProgress.ebookLocation || null,
|
progress: this.extraData?.progress || 0,
|
||||||
ebookProgress: oldMediaProgress.ebookProgress || null,
|
currentTime: this.currentTime,
|
||||||
isFinished: !!oldMediaProgress.isFinished,
|
isFinished: !!this.isFinished,
|
||||||
hideFromContinueListening: !!oldMediaProgress.hideFromContinueListening,
|
hideFromContinueListening: !!this.hideFromContinueListening,
|
||||||
finishedAt: oldMediaProgress.finishedAt,
|
ebookLocation: this.ebookLocation,
|
||||||
createdAt: oldMediaProgress.startedAt || oldMediaProgress.lastUpdate,
|
ebookProgress: this.ebookProgress,
|
||||||
updatedAt: oldMediaProgress.lastUpdate,
|
lastUpdate: this.updatedAt.valueOf(),
|
||||||
extraData: {
|
startedAt: this.createdAt.valueOf(),
|
||||||
libraryItemId: oldMediaProgress.libraryItemId,
|
finishedAt: this.finishedAt?.valueOf() || null
|
||||||
progress: oldMediaProgress.progress
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static removeById(mediaProgressId) {
|
|
||||||
return this.destroy({
|
|
||||||
where: {
|
|
||||||
id: mediaProgressId
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
getMediaItem(options) {
|
|
||||||
if (!this.mediaItemType) return Promise.resolve(null)
|
|
||||||
const mixinMethodName = `get${sequelize.uppercaseFirst(this.mediaItemType)}`
|
|
||||||
return this[mixinMethodName](options)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static upsertFromOld(oldMediaProgress) {
|
||||||
|
const mediaProgress = this.getFromOld(oldMediaProgress)
|
||||||
|
return this.upsert(mediaProgress)
|
||||||
|
}
|
||||||
|
|
||||||
MediaProgress.init({
|
static getFromOld(oldMediaProgress) {
|
||||||
id: {
|
return {
|
||||||
type: DataTypes.UUID,
|
id: oldMediaProgress.id,
|
||||||
defaultValue: DataTypes.UUIDV4,
|
userId: oldMediaProgress.userId,
|
||||||
primaryKey: true
|
mediaItemId: oldMediaProgress.mediaItemId,
|
||||||
},
|
mediaItemType: oldMediaProgress.mediaItemType,
|
||||||
mediaItemId: DataTypes.UUIDV4,
|
duration: oldMediaProgress.duration,
|
||||||
mediaItemType: DataTypes.STRING,
|
currentTime: oldMediaProgress.currentTime,
|
||||||
duration: DataTypes.FLOAT,
|
ebookLocation: oldMediaProgress.ebookLocation || null,
|
||||||
currentTime: DataTypes.FLOAT,
|
ebookProgress: oldMediaProgress.ebookProgress || null,
|
||||||
isFinished: DataTypes.BOOLEAN,
|
isFinished: !!oldMediaProgress.isFinished,
|
||||||
hideFromContinueListening: DataTypes.BOOLEAN,
|
hideFromContinueListening: !!oldMediaProgress.hideFromContinueListening,
|
||||||
ebookLocation: DataTypes.STRING,
|
finishedAt: oldMediaProgress.finishedAt,
|
||||||
ebookProgress: DataTypes.FLOAT,
|
createdAt: oldMediaProgress.startedAt || oldMediaProgress.lastUpdate,
|
||||||
finishedAt: DataTypes.DATE,
|
updatedAt: oldMediaProgress.lastUpdate,
|
||||||
extraData: DataTypes.JSON
|
extraData: {
|
||||||
}, {
|
libraryItemId: oldMediaProgress.libraryItemId,
|
||||||
sequelize,
|
progress: oldMediaProgress.progress
|
||||||
modelName: 'mediaProgress',
|
|
||||||
indexes: [
|
|
||||||
{
|
|
||||||
fields: ['updatedAt']
|
|
||||||
}
|
}
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
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, {
|
static removeById(mediaProgressId) {
|
||||||
foreignKey: 'mediaItemId',
|
return this.destroy({
|
||||||
constraints: false,
|
where: {
|
||||||
scope: {
|
id: mediaProgressId
|
||||||
mediaItemType: 'podcastEpisode'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
MediaProgress.belongsTo(podcastEpisode, { foreignKey: 'mediaItemId', constraints: false })
|
|
||||||
|
|
||||||
MediaProgress.addHook('afterFind', findResult => {
|
|
||||||
if (!findResult) return
|
|
||||||
|
|
||||||
if (!Array.isArray(findResult)) findResult = [findResult]
|
|
||||||
|
|
||||||
for (const instance of findResult) {
|
|
||||||
if (instance.mediaItemType === 'book' && instance.book !== undefined) {
|
|
||||||
instance.mediaItem = instance.book
|
|
||||||
instance.dataValues.mediaItem = instance.dataValues.book
|
|
||||||
} else if (instance.mediaItemType === 'podcastEpisode' && instance.podcastEpisode !== undefined) {
|
|
||||||
instance.mediaItem = instance.podcastEpisode
|
|
||||||
instance.dataValues.mediaItem = instance.dataValues.podcastEpisode
|
|
||||||
}
|
}
|
||||||
// To prevent mistakes:
|
})
|
||||||
delete instance.book
|
}
|
||||||
delete instance.dataValues.book
|
|
||||||
delete instance.podcastEpisode
|
|
||||||
delete instance.dataValues.podcastEpisode
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
user.hasMany(MediaProgress, {
|
getMediaItem(options) {
|
||||||
onDelete: 'CASCADE'
|
if (!this.mediaItemType) return Promise.resolve(null)
|
||||||
})
|
const mixinMethodName = `get${this.sequelize.uppercaseFirst(this.mediaItemType)}`
|
||||||
MediaProgress.belongsTo(user)
|
return this[mixinMethodName](options)
|
||||||
|
}
|
||||||
|
|
||||||
return MediaProgress
|
/**
|
||||||
}
|
* Initialize model
|
||||||
|
*
|
||||||
|
* Polymorphic association: Book has many MediaProgress. PodcastEpisode has many MediaProgress.
|
||||||
|
* @see https://sequelize.org/docs/v6/advanced-association-concepts/polymorphic-associations/
|
||||||
|
*
|
||||||
|
* @param {import('../Database').sequelize} sequelize
|
||||||
|
*/
|
||||||
|
static init(sequelize) {
|
||||||
|
super.init({
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
mediaItemId: DataTypes.UUIDV4,
|
||||||
|
mediaItemType: DataTypes.STRING,
|
||||||
|
duration: DataTypes.FLOAT,
|
||||||
|
currentTime: DataTypes.FLOAT,
|
||||||
|
isFinished: DataTypes.BOOLEAN,
|
||||||
|
hideFromContinueListening: DataTypes.BOOLEAN,
|
||||||
|
ebookLocation: DataTypes.STRING,
|
||||||
|
ebookProgress: DataTypes.FLOAT,
|
||||||
|
finishedAt: DataTypes.DATE,
|
||||||
|
extraData: DataTypes.JSON
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
modelName: 'mediaProgress',
|
||||||
|
indexes: [
|
||||||
|
{
|
||||||
|
fields: ['updatedAt']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
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 (!findResult) return
|
||||||
|
|
||||||
|
if (!Array.isArray(findResult)) findResult = [findResult]
|
||||||
|
|
||||||
|
for (const instance of findResult) {
|
||||||
|
if (instance.mediaItemType === 'book' && instance.book !== undefined) {
|
||||||
|
instance.mediaItem = instance.book
|
||||||
|
instance.dataValues.mediaItem = instance.dataValues.book
|
||||||
|
} else if (instance.mediaItemType === 'podcastEpisode' && instance.podcastEpisode !== undefined) {
|
||||||
|
instance.mediaItem = instance.podcastEpisode
|
||||||
|
instance.dataValues.mediaItem = instance.dataValues.podcastEpisode
|
||||||
|
}
|
||||||
|
// To prevent mistakes:
|
||||||
|
delete instance.book
|
||||||
|
delete instance.dataValues.book
|
||||||
|
delete instance.podcastEpisode
|
||||||
|
delete instance.dataValues.podcastEpisode
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
user.hasMany(MediaProgress, {
|
||||||
|
onDelete: 'CASCADE'
|
||||||
|
})
|
||||||
|
MediaProgress.belongsTo(user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = MediaProgress
|
@ -2,197 +2,251 @@ const { DataTypes, Model } = require('sequelize')
|
|||||||
|
|
||||||
const oldPlaybackSession = require('../objects/PlaybackSession')
|
const oldPlaybackSession = require('../objects/PlaybackSession')
|
||||||
|
|
||||||
module.exports = (sequelize) => {
|
|
||||||
class PlaybackSession extends Model {
|
|
||||||
static async getOldPlaybackSessions(where = null) {
|
|
||||||
const playbackSessions = await this.findAll({
|
|
||||||
where,
|
|
||||||
include: [
|
|
||||||
{
|
|
||||||
model: sequelize.models.device
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
return playbackSessions.map(session => this.getOldPlaybackSession(session))
|
|
||||||
}
|
|
||||||
|
|
||||||
static async getById(sessionId) {
|
class PlaybackSession extends Model {
|
||||||
const playbackSession = await this.findByPk(sessionId, {
|
constructor(values, options) {
|
||||||
include: [
|
super(values, options)
|
||||||
{
|
|
||||||
model: sequelize.models.device
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
if (!playbackSession) return null
|
|
||||||
return this.getOldPlaybackSession(playbackSession)
|
|
||||||
}
|
|
||||||
|
|
||||||
static getOldPlaybackSession(playbackSessionExpanded) {
|
/** @type {UUIDV4} */
|
||||||
const isPodcastEpisode = playbackSessionExpanded.mediaItemType === 'podcastEpisode'
|
this.id
|
||||||
|
/** @type {UUIDV4} */
|
||||||
|
this.mediaItemId
|
||||||
|
/** @type {string} */
|
||||||
|
this.mediaItemType
|
||||||
|
/** @type {string} */
|
||||||
|
this.displayTitle
|
||||||
|
/** @type {string} */
|
||||||
|
this.displayAuthor
|
||||||
|
/** @type {number} */
|
||||||
|
this.duration
|
||||||
|
/** @type {number} */
|
||||||
|
this.playMethod
|
||||||
|
/** @type {string} */
|
||||||
|
this.mediaPlayer
|
||||||
|
/** @type {number} */
|
||||||
|
this.startTime
|
||||||
|
/** @type {number} */
|
||||||
|
this.currentTime
|
||||||
|
/** @type {string} */
|
||||||
|
this.serverVersion
|
||||||
|
/** @type {string} */
|
||||||
|
this.coverPath
|
||||||
|
/** @type {number} */
|
||||||
|
this.timeListening
|
||||||
|
/** @type {Object} */
|
||||||
|
this.mediaMetadata
|
||||||
|
/** @type {string} */
|
||||||
|
this.date
|
||||||
|
/** @type {string} */
|
||||||
|
this.dayOfWeek
|
||||||
|
/** @type {Object} */
|
||||||
|
this.extraData
|
||||||
|
/** @type {UUIDV4} */
|
||||||
|
this.userId
|
||||||
|
/** @type {UUIDV4} */
|
||||||
|
this.deviceId
|
||||||
|
/** @type {UUIDV4} */
|
||||||
|
this.libraryId
|
||||||
|
/** @type {Date} */
|
||||||
|
this.updatedAt
|
||||||
|
/** @type {Date} */
|
||||||
|
this.createdAt
|
||||||
|
}
|
||||||
|
|
||||||
return new oldPlaybackSession({
|
static async getOldPlaybackSessions(where = null) {
|
||||||
id: playbackSessionExpanded.id,
|
const playbackSessions = await this.findAll({
|
||||||
userId: playbackSessionExpanded.userId,
|
where,
|
||||||
libraryId: playbackSessionExpanded.libraryId,
|
include: [
|
||||||
libraryItemId: playbackSessionExpanded.extraData?.libraryItemId || null,
|
{
|
||||||
bookId: isPodcastEpisode ? null : playbackSessionExpanded.mediaItemId,
|
model: this.sequelize.models.device
|
||||||
episodeId: isPodcastEpisode ? playbackSessionExpanded.mediaItemId : null,
|
|
||||||
mediaType: isPodcastEpisode ? 'podcast' : 'book',
|
|
||||||
mediaMetadata: playbackSessionExpanded.mediaMetadata,
|
|
||||||
chapters: null,
|
|
||||||
displayTitle: playbackSessionExpanded.displayTitle,
|
|
||||||
displayAuthor: playbackSessionExpanded.displayAuthor,
|
|
||||||
coverPath: playbackSessionExpanded.coverPath,
|
|
||||||
duration: playbackSessionExpanded.duration,
|
|
||||||
playMethod: playbackSessionExpanded.playMethod,
|
|
||||||
mediaPlayer: playbackSessionExpanded.mediaPlayer,
|
|
||||||
deviceInfo: playbackSessionExpanded.device?.getOldDevice() || null,
|
|
||||||
serverVersion: playbackSessionExpanded.serverVersion,
|
|
||||||
date: playbackSessionExpanded.date,
|
|
||||||
dayOfWeek: playbackSessionExpanded.dayOfWeek,
|
|
||||||
timeListening: playbackSessionExpanded.timeListening,
|
|
||||||
startTime: playbackSessionExpanded.startTime,
|
|
||||||
currentTime: playbackSessionExpanded.currentTime,
|
|
||||||
startedAt: playbackSessionExpanded.createdAt.valueOf(),
|
|
||||||
updatedAt: playbackSessionExpanded.updatedAt.valueOf()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
static removeById(sessionId) {
|
|
||||||
return this.destroy({
|
|
||||||
where: {
|
|
||||||
id: sessionId
|
|
||||||
}
|
}
|
||||||
})
|
]
|
||||||
}
|
})
|
||||||
|
return playbackSessions.map(session => this.getOldPlaybackSession(session))
|
||||||
|
}
|
||||||
|
|
||||||
static createFromOld(oldPlaybackSession) {
|
static async getById(sessionId) {
|
||||||
const playbackSession = this.getFromOld(oldPlaybackSession)
|
const playbackSession = await this.findByPk(sessionId, {
|
||||||
return this.create(playbackSession)
|
include: [
|
||||||
}
|
{
|
||||||
|
model: this.sequelize.models.device
|
||||||
static updateFromOld(oldPlaybackSession) {
|
|
||||||
const playbackSession = this.getFromOld(oldPlaybackSession)
|
|
||||||
return this.update(playbackSession, {
|
|
||||||
where: {
|
|
||||||
id: playbackSession.id
|
|
||||||
}
|
}
|
||||||
})
|
]
|
||||||
}
|
})
|
||||||
|
if (!playbackSession) return null
|
||||||
|
return this.getOldPlaybackSession(playbackSession)
|
||||||
|
}
|
||||||
|
|
||||||
static getFromOld(oldPlaybackSession) {
|
static getOldPlaybackSession(playbackSessionExpanded) {
|
||||||
return {
|
const isPodcastEpisode = playbackSessionExpanded.mediaItemType === 'podcastEpisode'
|
||||||
id: oldPlaybackSession.id,
|
|
||||||
mediaItemId: oldPlaybackSession.episodeId || oldPlaybackSession.bookId,
|
return new oldPlaybackSession({
|
||||||
mediaItemType: oldPlaybackSession.episodeId ? 'podcastEpisode' : 'book',
|
id: playbackSessionExpanded.id,
|
||||||
libraryId: oldPlaybackSession.libraryId,
|
userId: playbackSessionExpanded.userId,
|
||||||
displayTitle: oldPlaybackSession.displayTitle,
|
libraryId: playbackSessionExpanded.libraryId,
|
||||||
displayAuthor: oldPlaybackSession.displayAuthor,
|
libraryItemId: playbackSessionExpanded.extraData?.libraryItemId || null,
|
||||||
duration: oldPlaybackSession.duration,
|
bookId: isPodcastEpisode ? null : playbackSessionExpanded.mediaItemId,
|
||||||
playMethod: oldPlaybackSession.playMethod,
|
episodeId: isPodcastEpisode ? playbackSessionExpanded.mediaItemId : null,
|
||||||
mediaPlayer: oldPlaybackSession.mediaPlayer,
|
mediaType: isPodcastEpisode ? 'podcast' : 'book',
|
||||||
startTime: oldPlaybackSession.startTime,
|
mediaMetadata: playbackSessionExpanded.mediaMetadata,
|
||||||
currentTime: oldPlaybackSession.currentTime,
|
chapters: null,
|
||||||
serverVersion: oldPlaybackSession.serverVersion || null,
|
displayTitle: playbackSessionExpanded.displayTitle,
|
||||||
createdAt: oldPlaybackSession.startedAt,
|
displayAuthor: playbackSessionExpanded.displayAuthor,
|
||||||
updatedAt: oldPlaybackSession.updatedAt,
|
coverPath: playbackSessionExpanded.coverPath,
|
||||||
userId: oldPlaybackSession.userId,
|
duration: playbackSessionExpanded.duration,
|
||||||
deviceId: oldPlaybackSession.deviceInfo?.id || null,
|
playMethod: playbackSessionExpanded.playMethod,
|
||||||
timeListening: oldPlaybackSession.timeListening,
|
mediaPlayer: playbackSessionExpanded.mediaPlayer,
|
||||||
coverPath: oldPlaybackSession.coverPath,
|
deviceInfo: playbackSessionExpanded.device?.getOldDevice() || null,
|
||||||
mediaMetadata: oldPlaybackSession.mediaMetadata,
|
serverVersion: playbackSessionExpanded.serverVersion,
|
||||||
date: oldPlaybackSession.date,
|
date: playbackSessionExpanded.date,
|
||||||
dayOfWeek: oldPlaybackSession.dayOfWeek,
|
dayOfWeek: playbackSessionExpanded.dayOfWeek,
|
||||||
extraData: {
|
timeListening: playbackSessionExpanded.timeListening,
|
||||||
libraryItemId: oldPlaybackSession.libraryItemId
|
startTime: playbackSessionExpanded.startTime,
|
||||||
}
|
currentTime: playbackSessionExpanded.currentTime,
|
||||||
|
startedAt: playbackSessionExpanded.createdAt.valueOf(),
|
||||||
|
updatedAt: playbackSessionExpanded.updatedAt.valueOf()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static removeById(sessionId) {
|
||||||
|
return this.destroy({
|
||||||
|
where: {
|
||||||
|
id: sessionId
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
|
||||||
getMediaItem(options) {
|
static createFromOld(oldPlaybackSession) {
|
||||||
if (!this.mediaItemType) return Promise.resolve(null)
|
const playbackSession = this.getFromOld(oldPlaybackSession)
|
||||||
const mixinMethodName = `get${sequelize.uppercaseFirst(this.mediaItemType)}`
|
return this.create(playbackSession)
|
||||||
return this[mixinMethodName](options)
|
}
|
||||||
|
|
||||||
|
static updateFromOld(oldPlaybackSession) {
|
||||||
|
const playbackSession = this.getFromOld(oldPlaybackSession)
|
||||||
|
return this.update(playbackSession, {
|
||||||
|
where: {
|
||||||
|
id: playbackSession.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static getFromOld(oldPlaybackSession) {
|
||||||
|
return {
|
||||||
|
id: oldPlaybackSession.id,
|
||||||
|
mediaItemId: oldPlaybackSession.episodeId || oldPlaybackSession.bookId,
|
||||||
|
mediaItemType: oldPlaybackSession.episodeId ? 'podcastEpisode' : 'book',
|
||||||
|
libraryId: oldPlaybackSession.libraryId,
|
||||||
|
displayTitle: oldPlaybackSession.displayTitle,
|
||||||
|
displayAuthor: oldPlaybackSession.displayAuthor,
|
||||||
|
duration: oldPlaybackSession.duration,
|
||||||
|
playMethod: oldPlaybackSession.playMethod,
|
||||||
|
mediaPlayer: oldPlaybackSession.mediaPlayer,
|
||||||
|
startTime: oldPlaybackSession.startTime,
|
||||||
|
currentTime: oldPlaybackSession.currentTime,
|
||||||
|
serverVersion: oldPlaybackSession.serverVersion || null,
|
||||||
|
createdAt: oldPlaybackSession.startedAt,
|
||||||
|
updatedAt: oldPlaybackSession.updatedAt,
|
||||||
|
userId: oldPlaybackSession.userId,
|
||||||
|
deviceId: oldPlaybackSession.deviceInfo?.id || null,
|
||||||
|
timeListening: oldPlaybackSession.timeListening,
|
||||||
|
coverPath: oldPlaybackSession.coverPath,
|
||||||
|
mediaMetadata: oldPlaybackSession.mediaMetadata,
|
||||||
|
date: oldPlaybackSession.date,
|
||||||
|
dayOfWeek: oldPlaybackSession.dayOfWeek,
|
||||||
|
extraData: {
|
||||||
|
libraryItemId: oldPlaybackSession.libraryItemId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PlaybackSession.init({
|
getMediaItem(options) {
|
||||||
id: {
|
if (!this.mediaItemType) return Promise.resolve(null)
|
||||||
type: DataTypes.UUID,
|
const mixinMethodName = `get${this.sequelize.uppercaseFirst(this.mediaItemType)}`
|
||||||
defaultValue: DataTypes.UUIDV4,
|
return this[mixinMethodName](options)
|
||||||
primaryKey: true
|
}
|
||||||
},
|
|
||||||
mediaItemId: DataTypes.UUIDV4,
|
|
||||||
mediaItemType: DataTypes.STRING,
|
|
||||||
displayTitle: DataTypes.STRING,
|
|
||||||
displayAuthor: DataTypes.STRING,
|
|
||||||
duration: DataTypes.FLOAT,
|
|
||||||
playMethod: DataTypes.INTEGER,
|
|
||||||
mediaPlayer: DataTypes.STRING,
|
|
||||||
startTime: DataTypes.FLOAT,
|
|
||||||
currentTime: DataTypes.FLOAT,
|
|
||||||
serverVersion: DataTypes.STRING,
|
|
||||||
coverPath: DataTypes.STRING,
|
|
||||||
timeListening: DataTypes.INTEGER,
|
|
||||||
mediaMetadata: DataTypes.JSON,
|
|
||||||
date: DataTypes.STRING,
|
|
||||||
dayOfWeek: DataTypes.STRING,
|
|
||||||
extraData: DataTypes.JSON
|
|
||||||
}, {
|
|
||||||
sequelize,
|
|
||||||
modelName: 'playbackSession'
|
|
||||||
})
|
|
||||||
|
|
||||||
const { book, podcastEpisode, user, device, library } = sequelize.models
|
/**
|
||||||
|
* Initialize model
|
||||||
|
* @param {import('../Database').sequelize} sequelize
|
||||||
|
*/
|
||||||
|
static init(sequelize) {
|
||||||
|
super.init({
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
mediaItemId: DataTypes.UUIDV4,
|
||||||
|
mediaItemType: DataTypes.STRING,
|
||||||
|
displayTitle: DataTypes.STRING,
|
||||||
|
displayAuthor: DataTypes.STRING,
|
||||||
|
duration: DataTypes.FLOAT,
|
||||||
|
playMethod: DataTypes.INTEGER,
|
||||||
|
mediaPlayer: DataTypes.STRING,
|
||||||
|
startTime: DataTypes.FLOAT,
|
||||||
|
currentTime: DataTypes.FLOAT,
|
||||||
|
serverVersion: DataTypes.STRING,
|
||||||
|
coverPath: DataTypes.STRING,
|
||||||
|
timeListening: DataTypes.INTEGER,
|
||||||
|
mediaMetadata: DataTypes.JSON,
|
||||||
|
date: DataTypes.STRING,
|
||||||
|
dayOfWeek: DataTypes.STRING,
|
||||||
|
extraData: DataTypes.JSON
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
modelName: 'playbackSession'
|
||||||
|
})
|
||||||
|
|
||||||
user.hasMany(PlaybackSession)
|
const { book, podcastEpisode, user, device, library } = sequelize.models
|
||||||
PlaybackSession.belongsTo(user)
|
|
||||||
|
|
||||||
device.hasMany(PlaybackSession)
|
user.hasMany(PlaybackSession)
|
||||||
PlaybackSession.belongsTo(device)
|
PlaybackSession.belongsTo(user)
|
||||||
|
|
||||||
library.hasMany(PlaybackSession)
|
device.hasMany(PlaybackSession)
|
||||||
PlaybackSession.belongsTo(library)
|
PlaybackSession.belongsTo(device)
|
||||||
|
|
||||||
book.hasMany(PlaybackSession, {
|
library.hasMany(PlaybackSession)
|
||||||
foreignKey: 'mediaItemId',
|
PlaybackSession.belongsTo(library)
|
||||||
constraints: false,
|
|
||||||
scope: {
|
|
||||||
mediaItemType: 'book'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
PlaybackSession.belongsTo(book, { foreignKey: 'mediaItemId', constraints: false })
|
|
||||||
|
|
||||||
podcastEpisode.hasOne(PlaybackSession, {
|
book.hasMany(PlaybackSession, {
|
||||||
foreignKey: 'mediaItemId',
|
foreignKey: 'mediaItemId',
|
||||||
constraints: false,
|
constraints: false,
|
||||||
scope: {
|
scope: {
|
||||||
mediaItemType: 'podcastEpisode'
|
mediaItemType: 'book'
|
||||||
}
|
|
||||||
})
|
|
||||||
PlaybackSession.belongsTo(podcastEpisode, { foreignKey: 'mediaItemId', constraints: false })
|
|
||||||
|
|
||||||
PlaybackSession.addHook('afterFind', findResult => {
|
|
||||||
if (!findResult) return
|
|
||||||
|
|
||||||
if (!Array.isArray(findResult)) findResult = [findResult]
|
|
||||||
|
|
||||||
for (const instance of findResult) {
|
|
||||||
if (instance.mediaItemType === 'book' && instance.book !== undefined) {
|
|
||||||
instance.mediaItem = instance.book
|
|
||||||
instance.dataValues.mediaItem = instance.dataValues.book
|
|
||||||
} else if (instance.mediaItemType === 'podcastEpisode' && instance.podcastEpisode !== undefined) {
|
|
||||||
instance.mediaItem = instance.podcastEpisode
|
|
||||||
instance.dataValues.mediaItem = instance.dataValues.podcastEpisode
|
|
||||||
}
|
}
|
||||||
// To prevent mistakes:
|
})
|
||||||
delete instance.book
|
PlaybackSession.belongsTo(book, { foreignKey: 'mediaItemId', constraints: false })
|
||||||
delete instance.dataValues.book
|
|
||||||
delete instance.podcastEpisode
|
|
||||||
delete instance.dataValues.podcastEpisode
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return PlaybackSession
|
podcastEpisode.hasOne(PlaybackSession, {
|
||||||
}
|
foreignKey: 'mediaItemId',
|
||||||
|
constraints: false,
|
||||||
|
scope: {
|
||||||
|
mediaItemType: 'podcastEpisode'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
PlaybackSession.belongsTo(podcastEpisode, { foreignKey: 'mediaItemId', constraints: false })
|
||||||
|
|
||||||
|
PlaybackSession.addHook('afterFind', findResult => {
|
||||||
|
if (!findResult) return
|
||||||
|
|
||||||
|
if (!Array.isArray(findResult)) findResult = [findResult]
|
||||||
|
|
||||||
|
for (const instance of findResult) {
|
||||||
|
if (instance.mediaItemType === 'book' && instance.book !== undefined) {
|
||||||
|
instance.mediaItem = instance.book
|
||||||
|
instance.dataValues.mediaItem = instance.dataValues.book
|
||||||
|
} else if (instance.mediaItemType === 'podcastEpisode' && instance.podcastEpisode !== undefined) {
|
||||||
|
instance.mediaItem = instance.podcastEpisode
|
||||||
|
instance.dataValues.mediaItem = instance.dataValues.podcastEpisode
|
||||||
|
}
|
||||||
|
// To prevent mistakes:
|
||||||
|
delete instance.book
|
||||||
|
delete instance.dataValues.book
|
||||||
|
delete instance.podcastEpisode
|
||||||
|
delete instance.dataValues.podcastEpisode
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = PlaybackSession
|
||||||
|
@ -3,318 +3,341 @@ const Logger = require('../Logger')
|
|||||||
|
|
||||||
const oldPlaylist = require('../objects/Playlist')
|
const oldPlaylist = require('../objects/Playlist')
|
||||||
|
|
||||||
module.exports = (sequelize) => {
|
class Playlist extends Model {
|
||||||
class Playlist extends Model {
|
constructor(values, options) {
|
||||||
static async getOldPlaylists() {
|
super(values, options)
|
||||||
const playlists = await this.findAll({
|
|
||||||
include: {
|
|
||||||
model: sequelize.models.playlistMediaItem,
|
|
||||||
include: [
|
|
||||||
{
|
|
||||||
model: sequelize.models.book,
|
|
||||||
include: sequelize.models.libraryItem
|
|
||||||
},
|
|
||||||
{
|
|
||||||
model: sequelize.models.podcastEpisode,
|
|
||||||
include: {
|
|
||||||
model: sequelize.models.podcast,
|
|
||||||
include: sequelize.models.libraryItem
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
order: [['playlistMediaItems', 'order', 'ASC']]
|
|
||||||
})
|
|
||||||
return playlists.map(p => this.getOldPlaylist(p))
|
|
||||||
}
|
|
||||||
|
|
||||||
static getOldPlaylist(playlistExpanded) {
|
/** @type {UUIDV4} */
|
||||||
const items = playlistExpanded.playlistMediaItems.map(pmi => {
|
this.id
|
||||||
const libraryItemId = pmi.mediaItem?.podcast?.libraryItem?.id || pmi.mediaItem?.libraryItem?.id || null
|
/** @type {string} */
|
||||||
if (!libraryItemId) {
|
this.name
|
||||||
Logger.error(`[Playlist] Invalid playlist media item - No library item id found`, JSON.stringify(pmi, null, 2))
|
/** @type {string} */
|
||||||
return null
|
this.description
|
||||||
}
|
/** @type {UUIDV4} */
|
||||||
return {
|
this.libraryId
|
||||||
episodeId: pmi.mediaItemType === 'podcastEpisode' ? pmi.mediaItemId : '',
|
/** @type {UUIDV4} */
|
||||||
libraryItemId
|
this.userId
|
||||||
}
|
/** @type {Date} */
|
||||||
}).filter(pmi => pmi)
|
this.createdAt
|
||||||
|
/** @type {Date} */
|
||||||
|
this.updatedAt
|
||||||
|
}
|
||||||
|
|
||||||
return new oldPlaylist({
|
static async getOldPlaylists() {
|
||||||
id: playlistExpanded.id,
|
const playlists = await this.findAll({
|
||||||
libraryId: playlistExpanded.libraryId,
|
include: {
|
||||||
userId: playlistExpanded.userId,
|
model: this.sequelize.models.playlistMediaItem,
|
||||||
name: playlistExpanded.name,
|
|
||||||
description: playlistExpanded.description,
|
|
||||||
items,
|
|
||||||
lastUpdate: playlistExpanded.updatedAt.valueOf(),
|
|
||||||
createdAt: playlistExpanded.createdAt.valueOf()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get old playlist toJSONExpanded
|
|
||||||
* @param {[string[]]} include
|
|
||||||
* @returns {Promise<object>} oldPlaylist.toJSONExpanded
|
|
||||||
*/
|
|
||||||
async getOldJsonExpanded(include) {
|
|
||||||
this.playlistMediaItems = await this.getPlaylistMediaItems({
|
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
model: sequelize.models.book,
|
model: this.sequelize.models.book,
|
||||||
include: sequelize.models.libraryItem
|
include: this.sequelize.models.libraryItem
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
model: sequelize.models.podcastEpisode,
|
model: this.sequelize.models.podcastEpisode,
|
||||||
include: {
|
include: {
|
||||||
model: sequelize.models.podcast,
|
model: this.sequelize.models.podcast,
|
||||||
include: sequelize.models.libraryItem
|
include: this.sequelize.models.libraryItem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
|
||||||
order: [['order', 'ASC']]
|
|
||||||
}) || []
|
|
||||||
|
|
||||||
const oldPlaylist = sequelize.models.playlist.getOldPlaylist(this)
|
|
||||||
const libraryItemIds = oldPlaylist.items.map(i => i.libraryItemId)
|
|
||||||
|
|
||||||
let libraryItems = await sequelize.models.libraryItem.getAllOldLibraryItems({
|
|
||||||
id: libraryItemIds
|
|
||||||
})
|
|
||||||
|
|
||||||
const playlistExpanded = oldPlaylist.toJSONExpanded(libraryItems)
|
|
||||||
|
|
||||||
if (include?.includes('rssfeed')) {
|
|
||||||
const feeds = await this.getFeeds()
|
|
||||||
if (feeds?.length) {
|
|
||||||
playlistExpanded.rssFeed = sequelize.models.feed.getOldFeed(feeds[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return playlistExpanded
|
|
||||||
}
|
|
||||||
|
|
||||||
static createFromOld(oldPlaylist) {
|
|
||||||
const playlist = this.getFromOld(oldPlaylist)
|
|
||||||
return this.create(playlist)
|
|
||||||
}
|
|
||||||
|
|
||||||
static getFromOld(oldPlaylist) {
|
|
||||||
return {
|
|
||||||
id: oldPlaylist.id,
|
|
||||||
name: oldPlaylist.name,
|
|
||||||
description: oldPlaylist.description,
|
|
||||||
userId: oldPlaylist.userId,
|
|
||||||
libraryId: oldPlaylist.libraryId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static removeById(playlistId) {
|
|
||||||
return this.destroy({
|
|
||||||
where: {
|
|
||||||
id: playlistId
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get playlist by id
|
|
||||||
* @param {string} playlistId
|
|
||||||
* @returns {Promise<oldPlaylist|null>} returns null if not found
|
|
||||||
*/
|
|
||||||
static async getById(playlistId) {
|
|
||||||
if (!playlistId) return null
|
|
||||||
const playlist = await this.findByPk(playlistId, {
|
|
||||||
include: {
|
|
||||||
model: sequelize.models.playlistMediaItem,
|
|
||||||
include: [
|
|
||||||
{
|
|
||||||
model: sequelize.models.book,
|
|
||||||
include: sequelize.models.libraryItem
|
|
||||||
},
|
|
||||||
{
|
|
||||||
model: sequelize.models.podcastEpisode,
|
|
||||||
include: {
|
|
||||||
model: sequelize.models.podcast,
|
|
||||||
include: sequelize.models.libraryItem
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
order: [['playlistMediaItems', 'order', 'ASC']]
|
|
||||||
})
|
|
||||||
if (!playlist) return null
|
|
||||||
return this.getOldPlaylist(playlist)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get playlists for user and optionally for library
|
|
||||||
* @param {string} userId
|
|
||||||
* @param {[string]} libraryId optional
|
|
||||||
* @returns {Promise<Playlist[]>}
|
|
||||||
*/
|
|
||||||
static async getPlaylistsForUserAndLibrary(userId, libraryId = null) {
|
|
||||||
if (!userId && !libraryId) return []
|
|
||||||
const whereQuery = {}
|
|
||||||
if (userId) {
|
|
||||||
whereQuery.userId = userId
|
|
||||||
}
|
|
||||||
if (libraryId) {
|
|
||||||
whereQuery.libraryId = libraryId
|
|
||||||
}
|
|
||||||
const playlists = await this.findAll({
|
|
||||||
where: whereQuery,
|
|
||||||
include: {
|
|
||||||
model: sequelize.models.playlistMediaItem,
|
|
||||||
include: [
|
|
||||||
{
|
|
||||||
model: sequelize.models.book,
|
|
||||||
include: sequelize.models.libraryItem
|
|
||||||
},
|
|
||||||
{
|
|
||||||
model: sequelize.models.podcastEpisode,
|
|
||||||
include: {
|
|
||||||
model: sequelize.models.podcast,
|
|
||||||
include: sequelize.models.libraryItem
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
order: [
|
|
||||||
[literal('name COLLATE NOCASE'), 'ASC'],
|
|
||||||
['playlistMediaItems', 'order', 'ASC']
|
|
||||||
]
|
]
|
||||||
})
|
},
|
||||||
return playlists
|
order: [['playlistMediaItems', 'order', 'ASC']]
|
||||||
}
|
})
|
||||||
|
return playlists.map(p => this.getOldPlaylist(p))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
static getOldPlaylist(playlistExpanded) {
|
||||||
* Get number of playlists for a user and library
|
const items = playlistExpanded.playlistMediaItems.map(pmi => {
|
||||||
* @param {string} userId
|
const libraryItemId = pmi.mediaItem?.podcast?.libraryItem?.id || pmi.mediaItem?.libraryItem?.id || null
|
||||||
* @param {string} libraryId
|
if (!libraryItemId) {
|
||||||
* @returns
|
Logger.error(`[Playlist] Invalid playlist media item - No library item id found`, JSON.stringify(pmi, null, 2))
|
||||||
*/
|
return null
|
||||||
static async getNumPlaylistsForUserAndLibrary(userId, libraryId) {
|
|
||||||
return this.count({
|
|
||||||
where: {
|
|
||||||
userId,
|
|
||||||
libraryId
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all playlists for mediaItemIds
|
|
||||||
* @param {string[]} mediaItemIds
|
|
||||||
* @returns {Promise<Playlist[]>}
|
|
||||||
*/
|
|
||||||
static async getPlaylistsForMediaItemIds(mediaItemIds) {
|
|
||||||
if (!mediaItemIds?.length) return []
|
|
||||||
|
|
||||||
const playlistMediaItemsExpanded = await sequelize.models.playlistMediaItem.findAll({
|
|
||||||
where: {
|
|
||||||
mediaItemId: {
|
|
||||||
[Op.in]: mediaItemIds
|
|
||||||
}
|
|
||||||
},
|
|
||||||
include: [
|
|
||||||
{
|
|
||||||
model: sequelize.models.playlist,
|
|
||||||
include: {
|
|
||||||
model: sequelize.models.playlistMediaItem,
|
|
||||||
include: [
|
|
||||||
{
|
|
||||||
model: sequelize.models.book,
|
|
||||||
include: sequelize.models.libraryItem
|
|
||||||
},
|
|
||||||
{
|
|
||||||
model: sequelize.models.podcastEpisode,
|
|
||||||
include: {
|
|
||||||
model: sequelize.models.podcast,
|
|
||||||
include: sequelize.models.libraryItem
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
order: [['playlist', 'playlistMediaItems', 'order', 'ASC']]
|
|
||||||
})
|
|
||||||
|
|
||||||
const playlists = []
|
|
||||||
for (const playlistMediaItem of playlistMediaItemsExpanded) {
|
|
||||||
const playlist = playlistMediaItem.playlist
|
|
||||||
if (playlists.some(p => p.id === playlist.id)) continue
|
|
||||||
|
|
||||||
playlist.playlistMediaItems = playlist.playlistMediaItems.map(pmi => {
|
|
||||||
if (pmi.mediaItemType === 'book' && pmi.book !== undefined) {
|
|
||||||
pmi.mediaItem = pmi.book
|
|
||||||
pmi.dataValues.mediaItem = pmi.dataValues.book
|
|
||||||
} else if (pmi.mediaItemType === 'podcastEpisode' && pmi.podcastEpisode !== undefined) {
|
|
||||||
pmi.mediaItem = pmi.podcastEpisode
|
|
||||||
pmi.dataValues.mediaItem = pmi.dataValues.podcastEpisode
|
|
||||||
}
|
|
||||||
delete pmi.book
|
|
||||||
delete pmi.dataValues.book
|
|
||||||
delete pmi.podcastEpisode
|
|
||||||
delete pmi.dataValues.podcastEpisode
|
|
||||||
return pmi
|
|
||||||
})
|
|
||||||
playlists.push(playlist)
|
|
||||||
}
|
}
|
||||||
return playlists
|
return {
|
||||||
|
episodeId: pmi.mediaItemType === 'podcastEpisode' ? pmi.mediaItemId : '',
|
||||||
|
libraryItemId
|
||||||
|
}
|
||||||
|
}).filter(pmi => pmi)
|
||||||
|
|
||||||
|
return new oldPlaylist({
|
||||||
|
id: playlistExpanded.id,
|
||||||
|
libraryId: playlistExpanded.libraryId,
|
||||||
|
userId: playlistExpanded.userId,
|
||||||
|
name: playlistExpanded.name,
|
||||||
|
description: playlistExpanded.description,
|
||||||
|
items,
|
||||||
|
lastUpdate: playlistExpanded.updatedAt.valueOf(),
|
||||||
|
createdAt: playlistExpanded.createdAt.valueOf()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get old playlist toJSONExpanded
|
||||||
|
* @param {[string[]]} include
|
||||||
|
* @returns {Promise<object>} oldPlaylist.toJSONExpanded
|
||||||
|
*/
|
||||||
|
async getOldJsonExpanded(include) {
|
||||||
|
this.playlistMediaItems = await this.getPlaylistMediaItems({
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: this.sequelize.models.book,
|
||||||
|
include: this.sequelize.models.libraryItem
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: this.sequelize.models.podcastEpisode,
|
||||||
|
include: {
|
||||||
|
model: this.sequelize.models.podcast,
|
||||||
|
include: this.sequelize.models.libraryItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
order: [['order', 'ASC']]
|
||||||
|
}) || []
|
||||||
|
|
||||||
|
const oldPlaylist = this.sequelize.models.playlist.getOldPlaylist(this)
|
||||||
|
const libraryItemIds = oldPlaylist.items.map(i => i.libraryItemId)
|
||||||
|
|
||||||
|
let libraryItems = await this.sequelize.models.libraryItem.getAllOldLibraryItems({
|
||||||
|
id: libraryItemIds
|
||||||
|
})
|
||||||
|
|
||||||
|
const playlistExpanded = oldPlaylist.toJSONExpanded(libraryItems)
|
||||||
|
|
||||||
|
if (include?.includes('rssfeed')) {
|
||||||
|
const feeds = await this.getFeeds()
|
||||||
|
if (feeds?.length) {
|
||||||
|
playlistExpanded.rssFeed = this.sequelize.models.feed.getOldFeed(feeds[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return playlistExpanded
|
||||||
|
}
|
||||||
|
|
||||||
|
static createFromOld(oldPlaylist) {
|
||||||
|
const playlist = this.getFromOld(oldPlaylist)
|
||||||
|
return this.create(playlist)
|
||||||
|
}
|
||||||
|
|
||||||
|
static getFromOld(oldPlaylist) {
|
||||||
|
return {
|
||||||
|
id: oldPlaylist.id,
|
||||||
|
name: oldPlaylist.name,
|
||||||
|
description: oldPlaylist.description,
|
||||||
|
userId: oldPlaylist.userId,
|
||||||
|
libraryId: oldPlaylist.libraryId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Playlist.init({
|
static removeById(playlistId) {
|
||||||
id: {
|
return this.destroy({
|
||||||
type: DataTypes.UUID,
|
where: {
|
||||||
defaultValue: DataTypes.UUIDV4,
|
id: playlistId
|
||||||
primaryKey: true
|
|
||||||
},
|
|
||||||
name: DataTypes.STRING,
|
|
||||||
description: DataTypes.TEXT
|
|
||||||
}, {
|
|
||||||
sequelize,
|
|
||||||
modelName: 'playlist'
|
|
||||||
})
|
|
||||||
|
|
||||||
const { library, user } = sequelize.models
|
|
||||||
library.hasMany(Playlist)
|
|
||||||
Playlist.belongsTo(library)
|
|
||||||
|
|
||||||
user.hasMany(Playlist, {
|
|
||||||
onDelete: 'CASCADE'
|
|
||||||
})
|
|
||||||
Playlist.belongsTo(user)
|
|
||||||
|
|
||||||
Playlist.addHook('afterFind', findResult => {
|
|
||||||
if (!findResult) return
|
|
||||||
|
|
||||||
if (!Array.isArray(findResult)) findResult = [findResult]
|
|
||||||
|
|
||||||
for (const instance of findResult) {
|
|
||||||
if (instance.playlistMediaItems?.length) {
|
|
||||||
instance.playlistMediaItems = instance.playlistMediaItems.map(pmi => {
|
|
||||||
if (pmi.mediaItemType === 'book' && pmi.book !== undefined) {
|
|
||||||
pmi.mediaItem = pmi.book
|
|
||||||
pmi.dataValues.mediaItem = pmi.dataValues.book
|
|
||||||
} else if (pmi.mediaItemType === 'podcastEpisode' && pmi.podcastEpisode !== undefined) {
|
|
||||||
pmi.mediaItem = pmi.podcastEpisode
|
|
||||||
pmi.dataValues.mediaItem = pmi.dataValues.podcastEpisode
|
|
||||||
}
|
|
||||||
// To prevent mistakes:
|
|
||||||
delete pmi.book
|
|
||||||
delete pmi.dataValues.book
|
|
||||||
delete pmi.podcastEpisode
|
|
||||||
delete pmi.dataValues.podcastEpisode
|
|
||||||
return pmi
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get playlist by id
|
||||||
|
* @param {string} playlistId
|
||||||
|
* @returns {Promise<oldPlaylist|null>} returns null if not found
|
||||||
|
*/
|
||||||
|
static async getById(playlistId) {
|
||||||
|
if (!playlistId) return null
|
||||||
|
const playlist = await this.findByPk(playlistId, {
|
||||||
|
include: {
|
||||||
|
model: this.sequelize.models.playlistMediaItem,
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: this.sequelize.models.book,
|
||||||
|
include: this.sequelize.models.libraryItem
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: this.sequelize.models.podcastEpisode,
|
||||||
|
include: {
|
||||||
|
model: this.sequelize.models.podcast,
|
||||||
|
include: this.sequelize.models.libraryItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
order: [['playlistMediaItems', 'order', 'ASC']]
|
||||||
|
})
|
||||||
|
if (!playlist) return null
|
||||||
|
return this.getOldPlaylist(playlist)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get playlists for user and optionally for library
|
||||||
|
* @param {string} userId
|
||||||
|
* @param {[string]} libraryId optional
|
||||||
|
* @returns {Promise<Playlist[]>}
|
||||||
|
*/
|
||||||
|
static async getPlaylistsForUserAndLibrary(userId, libraryId = null) {
|
||||||
|
if (!userId && !libraryId) return []
|
||||||
|
const whereQuery = {}
|
||||||
|
if (userId) {
|
||||||
|
whereQuery.userId = userId
|
||||||
}
|
}
|
||||||
})
|
if (libraryId) {
|
||||||
|
whereQuery.libraryId = libraryId
|
||||||
|
}
|
||||||
|
const playlists = await this.findAll({
|
||||||
|
where: whereQuery,
|
||||||
|
include: {
|
||||||
|
model: this.sequelize.models.playlistMediaItem,
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: this.sequelize.models.book,
|
||||||
|
include: this.sequelize.models.libraryItem
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: this.sequelize.models.podcastEpisode,
|
||||||
|
include: {
|
||||||
|
model: this.sequelize.models.podcast,
|
||||||
|
include: this.sequelize.models.libraryItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
order: [
|
||||||
|
[literal('name COLLATE NOCASE'), 'ASC'],
|
||||||
|
['playlistMediaItems', 'order', 'ASC']
|
||||||
|
]
|
||||||
|
})
|
||||||
|
return playlists
|
||||||
|
}
|
||||||
|
|
||||||
return Playlist
|
/**
|
||||||
}
|
* Get number of playlists for a user and library
|
||||||
|
* @param {string} userId
|
||||||
|
* @param {string} libraryId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
static async getNumPlaylistsForUserAndLibrary(userId, libraryId) {
|
||||||
|
return this.count({
|
||||||
|
where: {
|
||||||
|
userId,
|
||||||
|
libraryId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all playlists for mediaItemIds
|
||||||
|
* @param {string[]} mediaItemIds
|
||||||
|
* @returns {Promise<Playlist[]>}
|
||||||
|
*/
|
||||||
|
static async getPlaylistsForMediaItemIds(mediaItemIds) {
|
||||||
|
if (!mediaItemIds?.length) return []
|
||||||
|
|
||||||
|
const playlistMediaItemsExpanded = await this.sequelize.models.playlistMediaItem.findAll({
|
||||||
|
where: {
|
||||||
|
mediaItemId: {
|
||||||
|
[Op.in]: mediaItemIds
|
||||||
|
}
|
||||||
|
},
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: this.sequelize.models.playlist,
|
||||||
|
include: {
|
||||||
|
model: this.sequelize.models.playlistMediaItem,
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: this.sequelize.models.book,
|
||||||
|
include: this.sequelize.models.libraryItem
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: this.sequelize.models.podcastEpisode,
|
||||||
|
include: {
|
||||||
|
model: this.sequelize.models.podcast,
|
||||||
|
include: this.sequelize.models.libraryItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
order: [['playlist', 'playlistMediaItems', 'order', 'ASC']]
|
||||||
|
})
|
||||||
|
|
||||||
|
const playlists = []
|
||||||
|
for (const playlistMediaItem of playlistMediaItemsExpanded) {
|
||||||
|
const playlist = playlistMediaItem.playlist
|
||||||
|
if (playlists.some(p => p.id === playlist.id)) continue
|
||||||
|
|
||||||
|
playlist.playlistMediaItems = playlist.playlistMediaItems.map(pmi => {
|
||||||
|
if (pmi.mediaItemType === 'book' && pmi.book !== undefined) {
|
||||||
|
pmi.mediaItem = pmi.book
|
||||||
|
pmi.dataValues.mediaItem = pmi.dataValues.book
|
||||||
|
} else if (pmi.mediaItemType === 'podcastEpisode' && pmi.podcastEpisode !== undefined) {
|
||||||
|
pmi.mediaItem = pmi.podcastEpisode
|
||||||
|
pmi.dataValues.mediaItem = pmi.dataValues.podcastEpisode
|
||||||
|
}
|
||||||
|
delete pmi.book
|
||||||
|
delete pmi.dataValues.book
|
||||||
|
delete pmi.podcastEpisode
|
||||||
|
delete pmi.dataValues.podcastEpisode
|
||||||
|
return pmi
|
||||||
|
})
|
||||||
|
playlists.push(playlist)
|
||||||
|
}
|
||||||
|
return playlists
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize model
|
||||||
|
* @param {import('../Database').sequelize} sequelize
|
||||||
|
*/
|
||||||
|
static init(sequelize) {
|
||||||
|
super.init({
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
name: DataTypes.STRING,
|
||||||
|
description: DataTypes.TEXT
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
modelName: 'playlist'
|
||||||
|
})
|
||||||
|
|
||||||
|
const { library, user } = sequelize.models
|
||||||
|
library.hasMany(Playlist)
|
||||||
|
Playlist.belongsTo(library)
|
||||||
|
|
||||||
|
user.hasMany(Playlist, {
|
||||||
|
onDelete: 'CASCADE'
|
||||||
|
})
|
||||||
|
Playlist.belongsTo(user)
|
||||||
|
|
||||||
|
Playlist.addHook('afterFind', findResult => {
|
||||||
|
if (!findResult) return
|
||||||
|
|
||||||
|
if (!Array.isArray(findResult)) findResult = [findResult]
|
||||||
|
|
||||||
|
for (const instance of findResult) {
|
||||||
|
if (instance.playlistMediaItems?.length) {
|
||||||
|
instance.playlistMediaItems = instance.playlistMediaItems.map(pmi => {
|
||||||
|
if (pmi.mediaItemType === 'book' && pmi.book !== undefined) {
|
||||||
|
pmi.mediaItem = pmi.book
|
||||||
|
pmi.dataValues.mediaItem = pmi.dataValues.book
|
||||||
|
} else if (pmi.mediaItemType === 'podcastEpisode' && pmi.podcastEpisode !== undefined) {
|
||||||
|
pmi.mediaItem = pmi.podcastEpisode
|
||||||
|
pmi.dataValues.mediaItem = pmi.dataValues.podcastEpisode
|
||||||
|
}
|
||||||
|
// To prevent mistakes:
|
||||||
|
delete pmi.book
|
||||||
|
delete pmi.dataValues.book
|
||||||
|
delete pmi.podcastEpisode
|
||||||
|
delete pmi.dataValues.podcastEpisode
|
||||||
|
return pmi
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Playlist
|
@ -1,84 +1,105 @@
|
|||||||
const { DataTypes, Model } = require('sequelize')
|
const { DataTypes, Model } = require('sequelize')
|
||||||
|
|
||||||
module.exports = (sequelize) => {
|
class PlaylistMediaItem extends Model {
|
||||||
class PlaylistMediaItem extends Model {
|
constructor(values, options) {
|
||||||
static removeByIds(playlistId, mediaItemId) {
|
super(values, options)
|
||||||
return this.destroy({
|
|
||||||
where: {
|
|
||||||
playlistId,
|
|
||||||
mediaItemId
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
getMediaItem(options) {
|
/** @type {UUIDV4} */
|
||||||
if (!this.mediaItemType) return Promise.resolve(null)
|
this.id
|
||||||
const mixinMethodName = `get${sequelize.uppercaseFirst(this.mediaItemType)}`
|
/** @type {UUIDV4} */
|
||||||
return this[mixinMethodName](options)
|
this.mediaItemId
|
||||||
}
|
/** @type {string} */
|
||||||
|
this.mediaItemType
|
||||||
|
/** @type {number} */
|
||||||
|
this.order
|
||||||
|
/** @type {UUIDV4} */
|
||||||
|
this.playlistId
|
||||||
|
/** @type {Date} */
|
||||||
|
this.createdAt
|
||||||
}
|
}
|
||||||
|
|
||||||
PlaylistMediaItem.init({
|
static removeByIds(playlistId, mediaItemId) {
|
||||||
id: {
|
return this.destroy({
|
||||||
type: DataTypes.UUID,
|
where: {
|
||||||
defaultValue: DataTypes.UUIDV4,
|
playlistId,
|
||||||
primaryKey: true
|
mediaItemId
|
||||||
},
|
|
||||||
mediaItemId: DataTypes.UUIDV4,
|
|
||||||
mediaItemType: DataTypes.STRING,
|
|
||||||
order: DataTypes.INTEGER
|
|
||||||
}, {
|
|
||||||
sequelize,
|
|
||||||
timestamps: true,
|
|
||||||
updatedAt: false,
|
|
||||||
modelName: 'playlistMediaItem'
|
|
||||||
})
|
|
||||||
|
|
||||||
const { book, podcastEpisode, playlist } = sequelize.models
|
|
||||||
|
|
||||||
book.hasMany(PlaylistMediaItem, {
|
|
||||||
foreignKey: 'mediaItemId',
|
|
||||||
constraints: false,
|
|
||||||
scope: {
|
|
||||||
mediaItemType: 'book'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
PlaylistMediaItem.belongsTo(book, { foreignKey: 'mediaItemId', constraints: false })
|
|
||||||
|
|
||||||
podcastEpisode.hasOne(PlaylistMediaItem, {
|
|
||||||
foreignKey: 'mediaItemId',
|
|
||||||
constraints: false,
|
|
||||||
scope: {
|
|
||||||
mediaItemType: 'podcastEpisode'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
PlaylistMediaItem.belongsTo(podcastEpisode, { foreignKey: 'mediaItemId', constraints: false })
|
|
||||||
|
|
||||||
PlaylistMediaItem.addHook('afterFind', findResult => {
|
|
||||||
if (!findResult) return
|
|
||||||
|
|
||||||
if (!Array.isArray(findResult)) findResult = [findResult]
|
|
||||||
|
|
||||||
for (const instance of findResult) {
|
|
||||||
if (instance.mediaItemType === 'book' && instance.book !== undefined) {
|
|
||||||
instance.mediaItem = instance.book
|
|
||||||
instance.dataValues.mediaItem = instance.dataValues.book
|
|
||||||
} else if (instance.mediaItemType === 'podcastEpisode' && instance.podcastEpisode !== undefined) {
|
|
||||||
instance.mediaItem = instance.podcastEpisode
|
|
||||||
instance.dataValues.mediaItem = instance.dataValues.podcastEpisode
|
|
||||||
}
|
}
|
||||||
// To prevent mistakes:
|
})
|
||||||
delete instance.book
|
}
|
||||||
delete instance.dataValues.book
|
|
||||||
delete instance.podcastEpisode
|
|
||||||
delete instance.dataValues.podcastEpisode
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
playlist.hasMany(PlaylistMediaItem, {
|
getMediaItem(options) {
|
||||||
onDelete: 'CASCADE'
|
if (!this.mediaItemType) return Promise.resolve(null)
|
||||||
})
|
const mixinMethodName = `get${this.sequelize.uppercaseFirst(this.mediaItemType)}`
|
||||||
PlaylistMediaItem.belongsTo(playlist)
|
return this[mixinMethodName](options)
|
||||||
|
}
|
||||||
|
|
||||||
return PlaylistMediaItem
|
/**
|
||||||
}
|
* Initialize model
|
||||||
|
* @param {import('../Database').sequelize} sequelize
|
||||||
|
*/
|
||||||
|
static init(sequelize) {
|
||||||
|
super.init({
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
mediaItemId: DataTypes.UUIDV4,
|
||||||
|
mediaItemType: DataTypes.STRING,
|
||||||
|
order: DataTypes.INTEGER
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
timestamps: true,
|
||||||
|
updatedAt: false,
|
||||||
|
modelName: 'playlistMediaItem'
|
||||||
|
})
|
||||||
|
|
||||||
|
const { book, podcastEpisode, playlist } = sequelize.models
|
||||||
|
|
||||||
|
book.hasMany(PlaylistMediaItem, {
|
||||||
|
foreignKey: 'mediaItemId',
|
||||||
|
constraints: false,
|
||||||
|
scope: {
|
||||||
|
mediaItemType: 'book'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
PlaylistMediaItem.belongsTo(book, { foreignKey: 'mediaItemId', constraints: false })
|
||||||
|
|
||||||
|
podcastEpisode.hasOne(PlaylistMediaItem, {
|
||||||
|
foreignKey: 'mediaItemId',
|
||||||
|
constraints: false,
|
||||||
|
scope: {
|
||||||
|
mediaItemType: 'podcastEpisode'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
PlaylistMediaItem.belongsTo(podcastEpisode, { foreignKey: 'mediaItemId', constraints: false })
|
||||||
|
|
||||||
|
PlaylistMediaItem.addHook('afterFind', findResult => {
|
||||||
|
if (!findResult) return
|
||||||
|
|
||||||
|
if (!Array.isArray(findResult)) findResult = [findResult]
|
||||||
|
|
||||||
|
for (const instance of findResult) {
|
||||||
|
if (instance.mediaItemType === 'book' && instance.book !== undefined) {
|
||||||
|
instance.mediaItem = instance.book
|
||||||
|
instance.dataValues.mediaItem = instance.dataValues.book
|
||||||
|
} else if (instance.mediaItemType === 'podcastEpisode' && instance.podcastEpisode !== undefined) {
|
||||||
|
instance.mediaItem = instance.podcastEpisode
|
||||||
|
instance.dataValues.mediaItem = instance.dataValues.podcastEpisode
|
||||||
|
}
|
||||||
|
// To prevent mistakes:
|
||||||
|
delete instance.book
|
||||||
|
delete instance.dataValues.book
|
||||||
|
delete instance.podcastEpisode
|
||||||
|
delete instance.dataValues.podcastEpisode
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
playlist.hasMany(PlaylistMediaItem, {
|
||||||
|
onDelete: 'CASCADE'
|
||||||
|
})
|
||||||
|
PlaylistMediaItem.belongsTo(playlist)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = PlaylistMediaItem
|
||||||
|
@ -1,100 +1,155 @@
|
|||||||
const { DataTypes, Model } = require('sequelize')
|
const { DataTypes, Model } = require('sequelize')
|
||||||
|
|
||||||
module.exports = (sequelize) => {
|
class Podcast extends Model {
|
||||||
class Podcast extends Model {
|
constructor(values, options) {
|
||||||
static getOldPodcast(libraryItemExpanded) {
|
super(values, options)
|
||||||
const podcastExpanded = libraryItemExpanded.media
|
|
||||||
const podcastEpisodes = podcastExpanded.podcastEpisodes?.map(ep => ep.getOldPodcastEpisode(libraryItemExpanded.id)).sort((a, b) => a.index - b.index)
|
|
||||||
return {
|
|
||||||
id: podcastExpanded.id,
|
|
||||||
libraryItemId: libraryItemExpanded.id,
|
|
||||||
metadata: {
|
|
||||||
title: podcastExpanded.title,
|
|
||||||
author: podcastExpanded.author,
|
|
||||||
description: podcastExpanded.description,
|
|
||||||
releaseDate: podcastExpanded.releaseDate,
|
|
||||||
genres: podcastExpanded.genres,
|
|
||||||
feedUrl: podcastExpanded.feedURL,
|
|
||||||
imageUrl: podcastExpanded.imageURL,
|
|
||||||
itunesPageUrl: podcastExpanded.itunesPageURL,
|
|
||||||
itunesId: podcastExpanded.itunesId,
|
|
||||||
itunesArtistId: podcastExpanded.itunesArtistId,
|
|
||||||
explicit: podcastExpanded.explicit,
|
|
||||||
language: podcastExpanded.language,
|
|
||||||
type: podcastExpanded.podcastType
|
|
||||||
},
|
|
||||||
coverPath: podcastExpanded.coverPath,
|
|
||||||
tags: podcastExpanded.tags,
|
|
||||||
episodes: podcastEpisodes || [],
|
|
||||||
autoDownloadEpisodes: podcastExpanded.autoDownloadEpisodes,
|
|
||||||
autoDownloadSchedule: podcastExpanded.autoDownloadSchedule,
|
|
||||||
lastEpisodeCheck: podcastExpanded.lastEpisodeCheck?.valueOf() || null,
|
|
||||||
maxEpisodesToKeep: podcastExpanded.maxEpisodesToKeep,
|
|
||||||
maxNewEpisodesToDownload: podcastExpanded.maxNewEpisodesToDownload
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static getFromOld(oldPodcast) {
|
/** @type {UUIDV4} */
|
||||||
const oldPodcastMetadata = oldPodcast.metadata
|
this.id
|
||||||
return {
|
/** @type {string} */
|
||||||
id: oldPodcast.id,
|
this.title
|
||||||
title: oldPodcastMetadata.title,
|
/** @type {string} */
|
||||||
titleIgnorePrefix: oldPodcastMetadata.titleIgnorePrefix,
|
this.titleIgnorePrefix
|
||||||
author: oldPodcastMetadata.author,
|
/** @type {string} */
|
||||||
releaseDate: oldPodcastMetadata.releaseDate,
|
this.author
|
||||||
feedURL: oldPodcastMetadata.feedUrl,
|
/** @type {string} */
|
||||||
imageURL: oldPodcastMetadata.imageUrl,
|
this.releaseDate
|
||||||
description: oldPodcastMetadata.description,
|
/** @type {string} */
|
||||||
itunesPageURL: oldPodcastMetadata.itunesPageUrl,
|
this.feedURL
|
||||||
itunesId: oldPodcastMetadata.itunesId,
|
/** @type {string} */
|
||||||
itunesArtistId: oldPodcastMetadata.itunesArtistId,
|
this.imageURL
|
||||||
language: oldPodcastMetadata.language,
|
/** @type {string} */
|
||||||
podcastType: oldPodcastMetadata.type,
|
this.description
|
||||||
explicit: !!oldPodcastMetadata.explicit,
|
/** @type {string} */
|
||||||
autoDownloadEpisodes: !!oldPodcast.autoDownloadEpisodes,
|
this.itunesPageURL
|
||||||
autoDownloadSchedule: oldPodcast.autoDownloadSchedule,
|
/** @type {string} */
|
||||||
lastEpisodeCheck: oldPodcast.lastEpisodeCheck,
|
this.itunesId
|
||||||
maxEpisodesToKeep: oldPodcast.maxEpisodesToKeep,
|
/** @type {string} */
|
||||||
maxNewEpisodesToDownload: oldPodcast.maxNewEpisodesToDownload,
|
this.itunesArtistId
|
||||||
coverPath: oldPodcast.coverPath,
|
/** @type {string} */
|
||||||
tags: oldPodcast.tags,
|
this.language
|
||||||
genres: oldPodcastMetadata.genres
|
/** @type {string} */
|
||||||
}
|
this.podcastType
|
||||||
|
/** @type {boolean} */
|
||||||
|
this.explicit
|
||||||
|
/** @type {boolean} */
|
||||||
|
this.autoDownloadEpisodes
|
||||||
|
/** @type {string} */
|
||||||
|
this.autoDownloadSchedule
|
||||||
|
/** @type {Date} */
|
||||||
|
this.lastEpisodeCheck
|
||||||
|
/** @type {number} */
|
||||||
|
this.maxEpisodesToKeep
|
||||||
|
/** @type {string} */
|
||||||
|
this.coverPath
|
||||||
|
/** @type {Object} */
|
||||||
|
this.tags
|
||||||
|
/** @type {Object} */
|
||||||
|
this.genres
|
||||||
|
/** @type {Date} */
|
||||||
|
this.createdAt
|
||||||
|
/** @type {Date} */
|
||||||
|
this.updatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
static getOldPodcast(libraryItemExpanded) {
|
||||||
|
const podcastExpanded = libraryItemExpanded.media
|
||||||
|
const podcastEpisodes = podcastExpanded.podcastEpisodes?.map(ep => ep.getOldPodcastEpisode(libraryItemExpanded.id)).sort((a, b) => a.index - b.index)
|
||||||
|
return {
|
||||||
|
id: podcastExpanded.id,
|
||||||
|
libraryItemId: libraryItemExpanded.id,
|
||||||
|
metadata: {
|
||||||
|
title: podcastExpanded.title,
|
||||||
|
author: podcastExpanded.author,
|
||||||
|
description: podcastExpanded.description,
|
||||||
|
releaseDate: podcastExpanded.releaseDate,
|
||||||
|
genres: podcastExpanded.genres,
|
||||||
|
feedUrl: podcastExpanded.feedURL,
|
||||||
|
imageUrl: podcastExpanded.imageURL,
|
||||||
|
itunesPageUrl: podcastExpanded.itunesPageURL,
|
||||||
|
itunesId: podcastExpanded.itunesId,
|
||||||
|
itunesArtistId: podcastExpanded.itunesArtistId,
|
||||||
|
explicit: podcastExpanded.explicit,
|
||||||
|
language: podcastExpanded.language,
|
||||||
|
type: podcastExpanded.podcastType
|
||||||
|
},
|
||||||
|
coverPath: podcastExpanded.coverPath,
|
||||||
|
tags: podcastExpanded.tags,
|
||||||
|
episodes: podcastEpisodes || [],
|
||||||
|
autoDownloadEpisodes: podcastExpanded.autoDownloadEpisodes,
|
||||||
|
autoDownloadSchedule: podcastExpanded.autoDownloadSchedule,
|
||||||
|
lastEpisodeCheck: podcastExpanded.lastEpisodeCheck?.valueOf() || null,
|
||||||
|
maxEpisodesToKeep: podcastExpanded.maxEpisodesToKeep,
|
||||||
|
maxNewEpisodesToDownload: podcastExpanded.maxNewEpisodesToDownload
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Podcast.init({
|
static getFromOld(oldPodcast) {
|
||||||
id: {
|
const oldPodcastMetadata = oldPodcast.metadata
|
||||||
type: DataTypes.UUID,
|
return {
|
||||||
defaultValue: DataTypes.UUIDV4,
|
id: oldPodcast.id,
|
||||||
primaryKey: true
|
title: oldPodcastMetadata.title,
|
||||||
},
|
titleIgnorePrefix: oldPodcastMetadata.titleIgnorePrefix,
|
||||||
title: DataTypes.STRING,
|
author: oldPodcastMetadata.author,
|
||||||
titleIgnorePrefix: DataTypes.STRING,
|
releaseDate: oldPodcastMetadata.releaseDate,
|
||||||
author: DataTypes.STRING,
|
feedURL: oldPodcastMetadata.feedUrl,
|
||||||
releaseDate: DataTypes.STRING,
|
imageURL: oldPodcastMetadata.imageUrl,
|
||||||
feedURL: DataTypes.STRING,
|
description: oldPodcastMetadata.description,
|
||||||
imageURL: DataTypes.STRING,
|
itunesPageURL: oldPodcastMetadata.itunesPageUrl,
|
||||||
description: DataTypes.TEXT,
|
itunesId: oldPodcastMetadata.itunesId,
|
||||||
itunesPageURL: DataTypes.STRING,
|
itunesArtistId: oldPodcastMetadata.itunesArtistId,
|
||||||
itunesId: DataTypes.STRING,
|
language: oldPodcastMetadata.language,
|
||||||
itunesArtistId: DataTypes.STRING,
|
podcastType: oldPodcastMetadata.type,
|
||||||
language: DataTypes.STRING,
|
explicit: !!oldPodcastMetadata.explicit,
|
||||||
podcastType: DataTypes.STRING,
|
autoDownloadEpisodes: !!oldPodcast.autoDownloadEpisodes,
|
||||||
explicit: DataTypes.BOOLEAN,
|
autoDownloadSchedule: oldPodcast.autoDownloadSchedule,
|
||||||
|
lastEpisodeCheck: oldPodcast.lastEpisodeCheck,
|
||||||
|
maxEpisodesToKeep: oldPodcast.maxEpisodesToKeep,
|
||||||
|
maxNewEpisodesToDownload: oldPodcast.maxNewEpisodesToDownload,
|
||||||
|
coverPath: oldPodcast.coverPath,
|
||||||
|
tags: oldPodcast.tags,
|
||||||
|
genres: oldPodcastMetadata.genres
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
autoDownloadEpisodes: DataTypes.BOOLEAN,
|
/**
|
||||||
autoDownloadSchedule: DataTypes.STRING,
|
* Initialize model
|
||||||
lastEpisodeCheck: DataTypes.DATE,
|
* @param {import('../Database').sequelize} sequelize
|
||||||
maxEpisodesToKeep: DataTypes.INTEGER,
|
*/
|
||||||
maxNewEpisodesToDownload: DataTypes.INTEGER,
|
static init(sequelize) {
|
||||||
coverPath: DataTypes.STRING,
|
super.init({
|
||||||
tags: DataTypes.JSON,
|
id: {
|
||||||
genres: DataTypes.JSON
|
type: DataTypes.UUID,
|
||||||
}, {
|
defaultValue: DataTypes.UUIDV4,
|
||||||
sequelize,
|
primaryKey: true
|
||||||
modelName: 'podcast'
|
},
|
||||||
})
|
title: DataTypes.STRING,
|
||||||
|
titleIgnorePrefix: 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,
|
||||||
|
podcastType: DataTypes.STRING,
|
||||||
|
explicit: DataTypes.BOOLEAN,
|
||||||
|
|
||||||
return Podcast
|
autoDownloadEpisodes: DataTypes.BOOLEAN,
|
||||||
}
|
autoDownloadSchedule: DataTypes.STRING,
|
||||||
|
lastEpisodeCheck: DataTypes.DATE,
|
||||||
|
maxEpisodesToKeep: DataTypes.INTEGER,
|
||||||
|
maxNewEpisodesToDownload: DataTypes.INTEGER,
|
||||||
|
coverPath: DataTypes.STRING,
|
||||||
|
tags: DataTypes.JSON,
|
||||||
|
genres: DataTypes.JSON
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
modelName: 'podcast'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Podcast
|
@ -1,102 +1,149 @@
|
|||||||
const { DataTypes, Model } = require('sequelize')
|
const { DataTypes, Model } = require('sequelize')
|
||||||
|
|
||||||
module.exports = (sequelize) => {
|
class PodcastEpisode extends Model {
|
||||||
class PodcastEpisode extends Model {
|
constructor(values, options) {
|
||||||
getOldPodcastEpisode(libraryItemId = null) {
|
super(values, options)
|
||||||
let enclosure = null
|
|
||||||
if (this.enclosureURL) {
|
/** @type {UUIDV4} */
|
||||||
enclosure = {
|
this.id
|
||||||
url: this.enclosureURL,
|
/** @type {number} */
|
||||||
type: this.enclosureType,
|
this.index
|
||||||
length: this.enclosureSize !== null ? String(this.enclosureSize) : null
|
/** @type {string} */
|
||||||
}
|
this.season
|
||||||
}
|
/** @type {string} */
|
||||||
return {
|
this.episode
|
||||||
libraryItemId: libraryItemId || null,
|
/** @type {string} */
|
||||||
podcastId: this.podcastId,
|
this.episodeType
|
||||||
id: this.id,
|
/** @type {string} */
|
||||||
oldEpisodeId: this.extraData?.oldEpisodeId || null,
|
this.title
|
||||||
index: this.index,
|
/** @type {string} */
|
||||||
season: this.season,
|
this.subtitle
|
||||||
episode: this.episode,
|
/** @type {string} */
|
||||||
episodeType: this.episodeType,
|
this.description
|
||||||
title: this.title,
|
/** @type {string} */
|
||||||
subtitle: this.subtitle,
|
this.pubDate
|
||||||
description: this.description,
|
/** @type {string} */
|
||||||
enclosure,
|
this.enclosureURL
|
||||||
pubDate: this.pubDate,
|
/** @type {BigInt} */
|
||||||
chapters: this.chapters,
|
this.enclosureSize
|
||||||
audioFile: this.audioFile,
|
/** @type {string} */
|
||||||
publishedAt: this.publishedAt?.valueOf() || null,
|
this.enclosureType
|
||||||
addedAt: this.createdAt.valueOf(),
|
/** @type {Date} */
|
||||||
updatedAt: this.updatedAt.valueOf()
|
this.publishedAt
|
||||||
|
/** @type {Object} */
|
||||||
|
this.audioFile
|
||||||
|
/** @type {Object} */
|
||||||
|
this.chapters
|
||||||
|
/** @type {Object} */
|
||||||
|
this.extraData
|
||||||
|
/** @type {UUIDV4} */
|
||||||
|
this.podcastId
|
||||||
|
/** @type {Date} */
|
||||||
|
this.createdAt
|
||||||
|
/** @type {Date} */
|
||||||
|
this.updatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
getOldPodcastEpisode(libraryItemId = null) {
|
||||||
|
let enclosure = null
|
||||||
|
if (this.enclosureURL) {
|
||||||
|
enclosure = {
|
||||||
|
url: this.enclosureURL,
|
||||||
|
type: this.enclosureType,
|
||||||
|
length: this.enclosureSize !== null ? String(this.enclosureSize) : null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return {
|
||||||
static createFromOld(oldEpisode) {
|
libraryItemId: libraryItemId || null,
|
||||||
const podcastEpisode = this.getFromOld(oldEpisode)
|
podcastId: this.podcastId,
|
||||||
return this.create(podcastEpisode)
|
id: this.id,
|
||||||
}
|
oldEpisodeId: this.extraData?.oldEpisodeId || null,
|
||||||
|
index: this.index,
|
||||||
static getFromOld(oldEpisode) {
|
season: this.season,
|
||||||
const extraData = {}
|
episode: this.episode,
|
||||||
if (oldEpisode.oldEpisodeId) {
|
episodeType: this.episodeType,
|
||||||
extraData.oldEpisodeId = oldEpisode.oldEpisodeId
|
title: this.title,
|
||||||
}
|
subtitle: this.subtitle,
|
||||||
return {
|
description: this.description,
|
||||||
id: oldEpisode.id,
|
enclosure,
|
||||||
index: oldEpisode.index,
|
pubDate: this.pubDate,
|
||||||
season: oldEpisode.season,
|
chapters: this.chapters,
|
||||||
episode: oldEpisode.episode,
|
audioFile: this.audioFile,
|
||||||
episodeType: oldEpisode.episodeType,
|
publishedAt: this.publishedAt?.valueOf() || null,
|
||||||
title: oldEpisode.title,
|
addedAt: this.createdAt.valueOf(),
|
||||||
subtitle: oldEpisode.subtitle,
|
updatedAt: this.updatedAt.valueOf()
|
||||||
description: oldEpisode.description,
|
|
||||||
pubDate: oldEpisode.pubDate,
|
|
||||||
enclosureURL: oldEpisode.enclosure?.url || null,
|
|
||||||
enclosureSize: oldEpisode.enclosure?.length || null,
|
|
||||||
enclosureType: oldEpisode.enclosure?.type || null,
|
|
||||||
publishedAt: oldEpisode.publishedAt,
|
|
||||||
podcastId: oldEpisode.podcastId,
|
|
||||||
audioFile: oldEpisode.audioFile?.toJSON() || null,
|
|
||||||
chapters: oldEpisode.chapters,
|
|
||||||
extraData
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PodcastEpisode.init({
|
static createFromOld(oldEpisode) {
|
||||||
id: {
|
const podcastEpisode = this.getFromOld(oldEpisode)
|
||||||
type: DataTypes.UUID,
|
return this.create(podcastEpisode)
|
||||||
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,
|
|
||||||
enclosureSize: DataTypes.BIGINT,
|
|
||||||
enclosureType: DataTypes.STRING,
|
|
||||||
publishedAt: DataTypes.DATE,
|
|
||||||
|
|
||||||
audioFile: DataTypes.JSON,
|
static getFromOld(oldEpisode) {
|
||||||
chapters: DataTypes.JSON,
|
const extraData = {}
|
||||||
extraData: DataTypes.JSON
|
if (oldEpisode.oldEpisodeId) {
|
||||||
}, {
|
extraData.oldEpisodeId = oldEpisode.oldEpisodeId
|
||||||
sequelize,
|
}
|
||||||
modelName: 'podcastEpisode'
|
return {
|
||||||
})
|
id: oldEpisode.id,
|
||||||
|
index: oldEpisode.index,
|
||||||
|
season: oldEpisode.season,
|
||||||
|
episode: oldEpisode.episode,
|
||||||
|
episodeType: oldEpisode.episodeType,
|
||||||
|
title: oldEpisode.title,
|
||||||
|
subtitle: oldEpisode.subtitle,
|
||||||
|
description: oldEpisode.description,
|
||||||
|
pubDate: oldEpisode.pubDate,
|
||||||
|
enclosureURL: oldEpisode.enclosure?.url || null,
|
||||||
|
enclosureSize: oldEpisode.enclosure?.length || null,
|
||||||
|
enclosureType: oldEpisode.enclosure?.type || null,
|
||||||
|
publishedAt: oldEpisode.publishedAt,
|
||||||
|
podcastId: oldEpisode.podcastId,
|
||||||
|
audioFile: oldEpisode.audioFile?.toJSON() || null,
|
||||||
|
chapters: oldEpisode.chapters,
|
||||||
|
extraData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const { podcast } = sequelize.models
|
/**
|
||||||
podcast.hasMany(PodcastEpisode, {
|
* Initialize model
|
||||||
onDelete: 'CASCADE'
|
* @param {import('../Database').sequelize} sequelize
|
||||||
})
|
*/
|
||||||
PodcastEpisode.belongsTo(podcast)
|
static init(sequelize) {
|
||||||
|
super.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,
|
||||||
|
enclosureSize: DataTypes.BIGINT,
|
||||||
|
enclosureType: DataTypes.STRING,
|
||||||
|
publishedAt: DataTypes.DATE,
|
||||||
|
|
||||||
return PodcastEpisode
|
audioFile: DataTypes.JSON,
|
||||||
}
|
chapters: DataTypes.JSON,
|
||||||
|
extraData: DataTypes.JSON
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
modelName: 'podcastEpisode'
|
||||||
|
})
|
||||||
|
|
||||||
|
const { podcast } = sequelize.models
|
||||||
|
podcast.hasMany(PodcastEpisode, {
|
||||||
|
onDelete: 'CASCADE'
|
||||||
|
})
|
||||||
|
PodcastEpisode.belongsTo(podcast)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = PodcastEpisode
|
@ -2,81 +2,104 @@ const { DataTypes, Model } = require('sequelize')
|
|||||||
|
|
||||||
const oldSeries = require('../objects/entities/Series')
|
const oldSeries = require('../objects/entities/Series')
|
||||||
|
|
||||||
module.exports = (sequelize) => {
|
class Series extends Model {
|
||||||
class Series extends Model {
|
constructor(values, options) {
|
||||||
static async getAllOldSeries() {
|
super(values, options)
|
||||||
const series = await this.findAll()
|
|
||||||
return series.map(se => se.getOldSeries())
|
|
||||||
}
|
|
||||||
|
|
||||||
getOldSeries() {
|
/** @type {UUIDV4} */
|
||||||
return new oldSeries({
|
this.id
|
||||||
id: this.id,
|
/** @type {string} */
|
||||||
name: this.name,
|
this.name
|
||||||
description: this.description,
|
/** @type {string} */
|
||||||
libraryId: this.libraryId,
|
this.nameIgnorePrefix
|
||||||
addedAt: this.createdAt.valueOf(),
|
/** @type {string} */
|
||||||
updatedAt: this.updatedAt.valueOf()
|
this.description
|
||||||
})
|
/** @type {UUIDV4} */
|
||||||
}
|
this.libraryId
|
||||||
|
/** @type {Date} */
|
||||||
|
this.createdAt
|
||||||
|
/** @type {Date} */
|
||||||
|
this.updatedAt
|
||||||
|
}
|
||||||
|
|
||||||
static updateFromOld(oldSeries) {
|
static async getAllOldSeries() {
|
||||||
const series = this.getFromOld(oldSeries)
|
const series = await this.findAll()
|
||||||
return this.update(series, {
|
return series.map(se => se.getOldSeries())
|
||||||
where: {
|
}
|
||||||
id: series.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
static createFromOld(oldSeries) {
|
getOldSeries() {
|
||||||
const series = this.getFromOld(oldSeries)
|
return new oldSeries({
|
||||||
return this.create(series)
|
id: this.id,
|
||||||
}
|
name: this.name,
|
||||||
|
description: this.description,
|
||||||
|
libraryId: this.libraryId,
|
||||||
|
addedAt: this.createdAt.valueOf(),
|
||||||
|
updatedAt: this.updatedAt.valueOf()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
static createBulkFromOld(oldSeriesObjs) {
|
static updateFromOld(oldSeries) {
|
||||||
const series = oldSeriesObjs.map(this.getFromOld)
|
const series = this.getFromOld(oldSeries)
|
||||||
return this.bulkCreate(series)
|
return this.update(series, {
|
||||||
}
|
where: {
|
||||||
|
id: series.id
|
||||||
static getFromOld(oldSeries) {
|
|
||||||
return {
|
|
||||||
id: oldSeries.id,
|
|
||||||
name: oldSeries.name,
|
|
||||||
nameIgnorePrefix: oldSeries.nameIgnorePrefix,
|
|
||||||
description: oldSeries.description,
|
|
||||||
libraryId: oldSeries.libraryId
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
|
||||||
static removeById(seriesId) {
|
static createFromOld(oldSeries) {
|
||||||
return this.destroy({
|
const series = this.getFromOld(oldSeries)
|
||||||
where: {
|
return this.create(series)
|
||||||
id: seriesId
|
}
|
||||||
}
|
|
||||||
})
|
static createBulkFromOld(oldSeriesObjs) {
|
||||||
|
const series = oldSeriesObjs.map(this.getFromOld)
|
||||||
|
return this.bulkCreate(series)
|
||||||
|
}
|
||||||
|
|
||||||
|
static getFromOld(oldSeries) {
|
||||||
|
return {
|
||||||
|
id: oldSeries.id,
|
||||||
|
name: oldSeries.name,
|
||||||
|
nameIgnorePrefix: oldSeries.nameIgnorePrefix,
|
||||||
|
description: oldSeries.description,
|
||||||
|
libraryId: oldSeries.libraryId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Series.init({
|
static removeById(seriesId) {
|
||||||
id: {
|
return this.destroy({
|
||||||
type: DataTypes.UUID,
|
where: {
|
||||||
defaultValue: DataTypes.UUIDV4,
|
id: seriesId
|
||||||
primaryKey: true
|
}
|
||||||
},
|
})
|
||||||
name: DataTypes.STRING,
|
}
|
||||||
nameIgnorePrefix: DataTypes.STRING,
|
|
||||||
description: DataTypes.TEXT
|
|
||||||
}, {
|
|
||||||
sequelize,
|
|
||||||
modelName: 'series'
|
|
||||||
})
|
|
||||||
|
|
||||||
const { library } = sequelize.models
|
/**
|
||||||
library.hasMany(Series, {
|
* Initialize model
|
||||||
onDelete: 'CASCADE'
|
* @param {import('../Database').sequelize} sequelize
|
||||||
})
|
*/
|
||||||
Series.belongsTo(library)
|
static init(sequelize) {
|
||||||
|
super.init({
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
name: DataTypes.STRING,
|
||||||
|
nameIgnorePrefix: DataTypes.STRING,
|
||||||
|
description: DataTypes.TEXT
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
modelName: 'series'
|
||||||
|
})
|
||||||
|
|
||||||
return Series
|
const { library } = sequelize.models
|
||||||
}
|
library.hasMany(Series, {
|
||||||
|
onDelete: 'CASCADE'
|
||||||
|
})
|
||||||
|
Series.belongsTo(library)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Series
|
@ -4,42 +4,59 @@ const oldEmailSettings = require('../objects/settings/EmailSettings')
|
|||||||
const oldServerSettings = require('../objects/settings/ServerSettings')
|
const oldServerSettings = require('../objects/settings/ServerSettings')
|
||||||
const oldNotificationSettings = require('../objects/settings/NotificationSettings')
|
const oldNotificationSettings = require('../objects/settings/NotificationSettings')
|
||||||
|
|
||||||
module.exports = (sequelize) => {
|
class Setting extends Model {
|
||||||
class Setting extends Model {
|
constructor(values, options) {
|
||||||
static async getOldSettings() {
|
super(values, options)
|
||||||
const settings = (await this.findAll()).map(se => se.value)
|
|
||||||
|
/** @type {string} */
|
||||||
|
this.key
|
||||||
|
/** @type {Object} */
|
||||||
|
this.value
|
||||||
|
/** @type {Date} */
|
||||||
|
this.createdAt
|
||||||
|
/** @type {Date} */
|
||||||
|
this.updatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getOldSettings() {
|
||||||
|
const settings = (await this.findAll()).map(se => se.value)
|
||||||
|
|
||||||
|
|
||||||
const emailSettingsJson = settings.find(se => se.id === 'email-settings')
|
const emailSettingsJson = settings.find(se => se.id === 'email-settings')
|
||||||
const serverSettingsJson = settings.find(se => se.id === 'server-settings')
|
const serverSettingsJson = settings.find(se => se.id === 'server-settings')
|
||||||
const notificationSettingsJson = settings.find(se => se.id === 'notification-settings')
|
const notificationSettingsJson = settings.find(se => se.id === 'notification-settings')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
settings,
|
settings,
|
||||||
emailSettings: new oldEmailSettings(emailSettingsJson),
|
emailSettings: new oldEmailSettings(emailSettingsJson),
|
||||||
serverSettings: new oldServerSettings(serverSettingsJson),
|
serverSettings: new oldServerSettings(serverSettingsJson),
|
||||||
notificationSettings: new oldNotificationSettings(notificationSettingsJson)
|
notificationSettings: new oldNotificationSettings(notificationSettingsJson)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static updateSettingObj(setting) {
|
|
||||||
return this.upsert({
|
|
||||||
key: setting.id,
|
|
||||||
value: setting
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Setting.init({
|
static updateSettingObj(setting) {
|
||||||
key: {
|
return this.upsert({
|
||||||
type: DataTypes.STRING,
|
key: setting.id,
|
||||||
primaryKey: true
|
value: setting
|
||||||
},
|
})
|
||||||
value: DataTypes.JSON
|
}
|
||||||
}, {
|
|
||||||
sequelize,
|
|
||||||
modelName: 'setting'
|
|
||||||
})
|
|
||||||
|
|
||||||
return Setting
|
/**
|
||||||
}
|
* Initialize model
|
||||||
|
* @param {import('../Database').sequelize} sequelize
|
||||||
|
*/
|
||||||
|
static init(sequelize) {
|
||||||
|
super.init({
|
||||||
|
key: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
value: DataTypes.JSON
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
modelName: 'setting'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Setting
|
@ -3,238 +3,273 @@ const { DataTypes, Model, Op } = require('sequelize')
|
|||||||
const Logger = require('../Logger')
|
const Logger = require('../Logger')
|
||||||
const oldUser = require('../objects/user/User')
|
const oldUser = require('../objects/user/User')
|
||||||
|
|
||||||
module.exports = (sequelize) => {
|
class User extends Model {
|
||||||
class User extends Model {
|
constructor(values, options) {
|
||||||
/**
|
super(values, options)
|
||||||
* Get all oldUsers
|
|
||||||
* @returns {Promise<oldUser>}
|
|
||||||
*/
|
|
||||||
static async getOldUsers() {
|
|
||||||
const users = await this.findAll({
|
|
||||||
include: sequelize.models.mediaProgress
|
|
||||||
})
|
|
||||||
return users.map(u => this.getOldUser(u))
|
|
||||||
}
|
|
||||||
|
|
||||||
static getOldUser(userExpanded) {
|
/** @type {UUIDV4} */
|
||||||
const mediaProgress = userExpanded.mediaProgresses.map(mp => mp.getOldMediaProgress())
|
this.id
|
||||||
|
/** @type {string} */
|
||||||
|
this.username
|
||||||
|
/** @type {string} */
|
||||||
|
this.email
|
||||||
|
/** @type {string} */
|
||||||
|
this.pash
|
||||||
|
/** @type {string} */
|
||||||
|
this.type
|
||||||
|
/** @type {boolean} */
|
||||||
|
this.isActive
|
||||||
|
/** @type {boolean} */
|
||||||
|
this.isLocked
|
||||||
|
/** @type {Date} */
|
||||||
|
this.lastSeen
|
||||||
|
/** @type {Object} */
|
||||||
|
this.permissions
|
||||||
|
/** @type {Object} */
|
||||||
|
this.bookmarks
|
||||||
|
/** @type {Object} */
|
||||||
|
this.extraData
|
||||||
|
/** @type {Date} */
|
||||||
|
this.createdAt
|
||||||
|
/** @type {Date} */
|
||||||
|
this.updatedAt
|
||||||
|
}
|
||||||
|
|
||||||
const librariesAccessible = userExpanded.permissions?.librariesAccessible || []
|
/**
|
||||||
const itemTagsSelected = userExpanded.permissions?.itemTagsSelected || []
|
* Get all oldUsers
|
||||||
const permissions = userExpanded.permissions || {}
|
* @returns {Promise<oldUser>}
|
||||||
delete permissions.librariesAccessible
|
*/
|
||||||
delete permissions.itemTagsSelected
|
static async getOldUsers() {
|
||||||
|
const users = await this.findAll({
|
||||||
|
include: this.sequelize.models.mediaProgress
|
||||||
|
})
|
||||||
|
return users.map(u => this.getOldUser(u))
|
||||||
|
}
|
||||||
|
|
||||||
return new oldUser({
|
static getOldUser(userExpanded) {
|
||||||
id: userExpanded.id,
|
const mediaProgress = userExpanded.mediaProgresses.map(mp => mp.getOldMediaProgress())
|
||||||
oldUserId: userExpanded.extraData?.oldUserId || null,
|
|
||||||
username: userExpanded.username,
|
|
||||||
pash: userExpanded.pash,
|
|
||||||
type: userExpanded.type,
|
|
||||||
token: userExpanded.token,
|
|
||||||
mediaProgress,
|
|
||||||
seriesHideFromContinueListening: userExpanded.extraData?.seriesHideFromContinueListening || [],
|
|
||||||
bookmarks: userExpanded.bookmarks,
|
|
||||||
isActive: userExpanded.isActive,
|
|
||||||
isLocked: userExpanded.isLocked,
|
|
||||||
lastSeen: userExpanded.lastSeen?.valueOf() || null,
|
|
||||||
createdAt: userExpanded.createdAt.valueOf(),
|
|
||||||
permissions,
|
|
||||||
librariesAccessible,
|
|
||||||
itemTagsSelected
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
static createFromOld(oldUser) {
|
const librariesAccessible = userExpanded.permissions?.librariesAccessible || []
|
||||||
const user = this.getFromOld(oldUser)
|
const itemTagsSelected = userExpanded.permissions?.itemTagsSelected || []
|
||||||
return this.create(user)
|
const permissions = userExpanded.permissions || {}
|
||||||
}
|
delete permissions.librariesAccessible
|
||||||
|
delete permissions.itemTagsSelected
|
||||||
|
|
||||||
static updateFromOld(oldUser) {
|
return new oldUser({
|
||||||
const user = this.getFromOld(oldUser)
|
id: userExpanded.id,
|
||||||
return this.update(user, {
|
oldUserId: userExpanded.extraData?.oldUserId || null,
|
||||||
where: {
|
username: userExpanded.username,
|
||||||
id: user.id
|
pash: userExpanded.pash,
|
||||||
}
|
type: userExpanded.type,
|
||||||
}).then((result) => result[0] > 0).catch((error) => {
|
token: userExpanded.token,
|
||||||
Logger.error(`[User] Failed to save user ${oldUser.id}`, error)
|
mediaProgress,
|
||||||
return false
|
seriesHideFromContinueListening: userExpanded.extraData?.seriesHideFromContinueListening || [],
|
||||||
})
|
bookmarks: userExpanded.bookmarks,
|
||||||
}
|
isActive: userExpanded.isActive,
|
||||||
|
isLocked: userExpanded.isLocked,
|
||||||
|
lastSeen: userExpanded.lastSeen?.valueOf() || null,
|
||||||
|
createdAt: userExpanded.createdAt.valueOf(),
|
||||||
|
permissions,
|
||||||
|
librariesAccessible,
|
||||||
|
itemTagsSelected
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
static getFromOld(oldUser) {
|
static createFromOld(oldUser) {
|
||||||
return {
|
const user = this.getFromOld(oldUser)
|
||||||
id: oldUser.id,
|
return this.create(user)
|
||||||
username: oldUser.username,
|
}
|
||||||
pash: oldUser.pash || null,
|
|
||||||
type: oldUser.type || null,
|
static updateFromOld(oldUser) {
|
||||||
token: oldUser.token || null,
|
const user = this.getFromOld(oldUser)
|
||||||
isActive: !!oldUser.isActive,
|
return this.update(user, {
|
||||||
lastSeen: oldUser.lastSeen || null,
|
where: {
|
||||||
extraData: {
|
id: user.id
|
||||||
seriesHideFromContinueListening: oldUser.seriesHideFromContinueListening || [],
|
|
||||||
oldUserId: oldUser.oldUserId
|
|
||||||
},
|
|
||||||
createdAt: oldUser.createdAt || Date.now(),
|
|
||||||
permissions: {
|
|
||||||
...oldUser.permissions,
|
|
||||||
librariesAccessible: oldUser.librariesAccessible || [],
|
|
||||||
itemTagsSelected: oldUser.itemTagsSelected || []
|
|
||||||
},
|
|
||||||
bookmarks: oldUser.bookmarks
|
|
||||||
}
|
}
|
||||||
}
|
}).then((result) => result[0] > 0).catch((error) => {
|
||||||
|
Logger.error(`[User] Failed to save user ${oldUser.id}`, error)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
static removeById(userId) {
|
static getFromOld(oldUser) {
|
||||||
return this.destroy({
|
return {
|
||||||
where: {
|
id: oldUser.id,
|
||||||
id: userId
|
username: oldUser.username,
|
||||||
}
|
pash: oldUser.pash || null,
|
||||||
})
|
type: oldUser.type || null,
|
||||||
}
|
token: oldUser.token || null,
|
||||||
|
isActive: !!oldUser.isActive,
|
||||||
/**
|
lastSeen: oldUser.lastSeen || null,
|
||||||
* Create root user
|
extraData: {
|
||||||
* @param {string} username
|
seriesHideFromContinueListening: oldUser.seriesHideFromContinueListening || [],
|
||||||
* @param {string} pash
|
oldUserId: oldUser.oldUserId
|
||||||
* @param {Auth} auth
|
},
|
||||||
* @returns {oldUser}
|
createdAt: oldUser.createdAt || Date.now(),
|
||||||
*/
|
permissions: {
|
||||||
static async createRootUser(username, pash, auth) {
|
...oldUser.permissions,
|
||||||
const userId = uuidv4()
|
librariesAccessible: oldUser.librariesAccessible || [],
|
||||||
|
itemTagsSelected: oldUser.itemTagsSelected || []
|
||||||
const token = await auth.generateAccessToken({ userId, username })
|
},
|
||||||
|
bookmarks: oldUser.bookmarks
|
||||||
const newRoot = new oldUser({
|
|
||||||
id: userId,
|
|
||||||
type: 'root',
|
|
||||||
username,
|
|
||||||
pash,
|
|
||||||
token,
|
|
||||||
isActive: true,
|
|
||||||
createdAt: Date.now()
|
|
||||||
})
|
|
||||||
await this.createFromOld(newRoot)
|
|
||||||
return newRoot
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a user by id or by the old database id
|
|
||||||
* @temp User ids were updated in v2.3.0 migration and old API tokens may still use that id
|
|
||||||
* @param {string} userId
|
|
||||||
* @returns {Promise<oldUser|null>} null if not found
|
|
||||||
*/
|
|
||||||
static async getUserByIdOrOldId(userId) {
|
|
||||||
if (!userId) return null
|
|
||||||
const user = await this.findOne({
|
|
||||||
where: {
|
|
||||||
[Op.or]: [
|
|
||||||
{
|
|
||||||
id: userId
|
|
||||||
},
|
|
||||||
{
|
|
||||||
extraData: {
|
|
||||||
[Op.substring]: userId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
include: sequelize.models.mediaProgress
|
|
||||||
})
|
|
||||||
if (!user) return null
|
|
||||||
return this.getOldUser(user)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get user by username case insensitive
|
|
||||||
* @param {string} username
|
|
||||||
* @returns {Promise<oldUser|null>} returns null if not found
|
|
||||||
*/
|
|
||||||
static async getUserByUsername(username) {
|
|
||||||
if (!username) return null
|
|
||||||
const user = await this.findOne({
|
|
||||||
where: {
|
|
||||||
username: {
|
|
||||||
[Op.like]: username
|
|
||||||
}
|
|
||||||
},
|
|
||||||
include: sequelize.models.mediaProgress
|
|
||||||
})
|
|
||||||
if (!user) return null
|
|
||||||
return this.getOldUser(user)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get user by id
|
|
||||||
* @param {string} userId
|
|
||||||
* @returns {Promise<oldUser|null>} returns null if not found
|
|
||||||
*/
|
|
||||||
static async getUserById(userId) {
|
|
||||||
if (!userId) return null
|
|
||||||
const user = await this.findByPk(userId, {
|
|
||||||
include: sequelize.models.mediaProgress
|
|
||||||
})
|
|
||||||
if (!user) return null
|
|
||||||
return this.getOldUser(user)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get array of user id and username
|
|
||||||
* @returns {object[]} { id, username }
|
|
||||||
*/
|
|
||||||
static async getMinifiedUserObjects() {
|
|
||||||
const users = await this.findAll({
|
|
||||||
attributes: ['id', 'username']
|
|
||||||
})
|
|
||||||
return users.map(u => {
|
|
||||||
return {
|
|
||||||
id: u.id,
|
|
||||||
username: u.username
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if root user exists
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
static async getHasRootUser() {
|
|
||||||
const count = await this.count({
|
|
||||||
where: {
|
|
||||||
type: 'root'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return count > 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
User.init({
|
static removeById(userId) {
|
||||||
id: {
|
return this.destroy({
|
||||||
type: DataTypes.UUID,
|
where: {
|
||||||
defaultValue: DataTypes.UUIDV4,
|
id: userId
|
||||||
primaryKey: true
|
}
|
||||||
},
|
})
|
||||||
username: DataTypes.STRING,
|
}
|
||||||
email: DataTypes.STRING,
|
|
||||||
pash: DataTypes.STRING,
|
|
||||||
type: DataTypes.STRING,
|
|
||||||
token: DataTypes.STRING,
|
|
||||||
isActive: {
|
|
||||||
type: DataTypes.BOOLEAN,
|
|
||||||
defaultValue: false
|
|
||||||
},
|
|
||||||
isLocked: {
|
|
||||||
type: DataTypes.BOOLEAN,
|
|
||||||
defaultValue: false
|
|
||||||
},
|
|
||||||
lastSeen: DataTypes.DATE,
|
|
||||||
permissions: DataTypes.JSON,
|
|
||||||
bookmarks: DataTypes.JSON,
|
|
||||||
extraData: DataTypes.JSON
|
|
||||||
}, {
|
|
||||||
sequelize,
|
|
||||||
modelName: 'user'
|
|
||||||
})
|
|
||||||
|
|
||||||
return User
|
/**
|
||||||
}
|
* Create root user
|
||||||
|
* @param {string} username
|
||||||
|
* @param {string} pash
|
||||||
|
* @param {Auth} auth
|
||||||
|
* @returns {oldUser}
|
||||||
|
*/
|
||||||
|
static async createRootUser(username, pash, auth) {
|
||||||
|
const userId = uuidv4()
|
||||||
|
|
||||||
|
const token = await auth.generateAccessToken({ userId, username })
|
||||||
|
|
||||||
|
const newRoot = new oldUser({
|
||||||
|
id: userId,
|
||||||
|
type: 'root',
|
||||||
|
username,
|
||||||
|
pash,
|
||||||
|
token,
|
||||||
|
isActive: true,
|
||||||
|
createdAt: Date.now()
|
||||||
|
})
|
||||||
|
await this.createFromOld(newRoot)
|
||||||
|
return newRoot
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a user by id or by the old database id
|
||||||
|
* @temp User ids were updated in v2.3.0 migration and old API tokens may still use that id
|
||||||
|
* @param {string} userId
|
||||||
|
* @returns {Promise<oldUser|null>} null if not found
|
||||||
|
*/
|
||||||
|
static async getUserByIdOrOldId(userId) {
|
||||||
|
if (!userId) return null
|
||||||
|
const user = await this.findOne({
|
||||||
|
where: {
|
||||||
|
[Op.or]: [
|
||||||
|
{
|
||||||
|
id: userId
|
||||||
|
},
|
||||||
|
{
|
||||||
|
extraData: {
|
||||||
|
[Op.substring]: userId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
include: this.sequelize.models.mediaProgress
|
||||||
|
})
|
||||||
|
if (!user) return null
|
||||||
|
return this.getOldUser(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user by username case insensitive
|
||||||
|
* @param {string} username
|
||||||
|
* @returns {Promise<oldUser|null>} returns null if not found
|
||||||
|
*/
|
||||||
|
static async getUserByUsername(username) {
|
||||||
|
if (!username) return null
|
||||||
|
const user = await this.findOne({
|
||||||
|
where: {
|
||||||
|
username: {
|
||||||
|
[Op.like]: username
|
||||||
|
}
|
||||||
|
},
|
||||||
|
include: this.sequelize.models.mediaProgress
|
||||||
|
})
|
||||||
|
if (!user) return null
|
||||||
|
return this.getOldUser(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user by id
|
||||||
|
* @param {string} userId
|
||||||
|
* @returns {Promise<oldUser|null>} returns null if not found
|
||||||
|
*/
|
||||||
|
static async getUserById(userId) {
|
||||||
|
if (!userId) return null
|
||||||
|
const user = await this.findByPk(userId, {
|
||||||
|
include: this.sequelize.models.mediaProgress
|
||||||
|
})
|
||||||
|
if (!user) return null
|
||||||
|
return this.getOldUser(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get array of user id and username
|
||||||
|
* @returns {object[]} { id, username }
|
||||||
|
*/
|
||||||
|
static async getMinifiedUserObjects() {
|
||||||
|
const users = await this.findAll({
|
||||||
|
attributes: ['id', 'username']
|
||||||
|
})
|
||||||
|
return users.map(u => {
|
||||||
|
return {
|
||||||
|
id: u.id,
|
||||||
|
username: u.username
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if root user exists
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
static async getHasRootUser() {
|
||||||
|
const count = await this.count({
|
||||||
|
where: {
|
||||||
|
type: 'root'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return count > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize model
|
||||||
|
* @param {import('../Database').sequelize} sequelize
|
||||||
|
*/
|
||||||
|
static init(sequelize) {
|
||||||
|
super.init({
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
username: DataTypes.STRING,
|
||||||
|
email: DataTypes.STRING,
|
||||||
|
pash: DataTypes.STRING,
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
token: DataTypes.STRING,
|
||||||
|
isActive: {
|
||||||
|
type: DataTypes.BOOLEAN,
|
||||||
|
defaultValue: false
|
||||||
|
},
|
||||||
|
isLocked: {
|
||||||
|
type: DataTypes.BOOLEAN,
|
||||||
|
defaultValue: false
|
||||||
|
},
|
||||||
|
lastSeen: DataTypes.DATE,
|
||||||
|
permissions: DataTypes.JSON,
|
||||||
|
bookmarks: DataTypes.JSON,
|
||||||
|
extraData: DataTypes.JSON
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
modelName: 'user'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = User
|
Loading…
x
Reference in New Issue
Block a user