mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2024-11-08 09:04:43 +01:00
361 lines
11 KiB
JavaScript
361 lines
11 KiB
JavaScript
const { DataTypes, Model, Op } = require('sequelize')
|
|
const Logger = require('../Logger')
|
|
|
|
const oldPlaylist = require('../objects/Playlist')
|
|
const { areEquivalent } = require('../utils/index')
|
|
|
|
module.exports = (sequelize) => {
|
|
class Playlist extends Model {
|
|
static async getOldPlaylists() {
|
|
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) {
|
|
const items = playlistExpanded.playlistMediaItems.map(pmi => {
|
|
const libraryItemId = pmi.mediaItem?.podcast?.libraryItem?.id || pmi.mediaItem?.libraryItem?.id || null
|
|
if (!libraryItemId) {
|
|
Logger.error(`[Playlist] Invalid playlist media item - No library item id found`, JSON.stringify(pmi, null, 2))
|
|
return null
|
|
}
|
|
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: sequelize.models.book,
|
|
include: sequelize.models.libraryItem
|
|
},
|
|
{
|
|
model: sequelize.models.podcastEpisode,
|
|
include: {
|
|
model: sequelize.models.podcast,
|
|
include: 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 async fullUpdateFromOld(oldPlaylist, playlistMediaItems) {
|
|
const existingPlaylist = await this.findByPk(oldPlaylist.id, {
|
|
include: sequelize.models.playlistMediaItem
|
|
})
|
|
if (!existingPlaylist) return false
|
|
|
|
let hasUpdates = false
|
|
const playlist = this.getFromOld(oldPlaylist)
|
|
|
|
for (const pmi of playlistMediaItems) {
|
|
const existingPmi = existingPlaylist.playlistMediaItems.find(i => i.mediaItemId === pmi.mediaItemId)
|
|
if (!existingPmi) {
|
|
await sequelize.models.playlistMediaItem.create(pmi)
|
|
hasUpdates = true
|
|
} else if (existingPmi.order != pmi.order) {
|
|
await existingPmi.update({ order: pmi.order })
|
|
hasUpdates = true
|
|
}
|
|
}
|
|
for (const pmi of existingPlaylist.playlistMediaItems) {
|
|
// Pmi was removed
|
|
if (!playlistMediaItems.some(i => i.mediaItemId === pmi.mediaItemId)) {
|
|
await pmi.destroy()
|
|
hasUpdates = true
|
|
}
|
|
}
|
|
|
|
let hasPlaylistUpdates = false
|
|
for (const key in playlist) {
|
|
let existingValue = existingPlaylist[key]
|
|
if (existingValue instanceof Date) existingValue = existingValue.valueOf()
|
|
|
|
if (!areEquivalent(playlist[key], existingValue)) {
|
|
hasPlaylistUpdates = true
|
|
}
|
|
}
|
|
if (hasPlaylistUpdates) {
|
|
existingPlaylist.update(playlist)
|
|
hasUpdates = true
|
|
}
|
|
return hasUpdates
|
|
}
|
|
|
|
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<oldPlaylist[]>}
|
|
*/
|
|
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: [['playlistMediaItems', 'order', 'ASC']]
|
|
})
|
|
return playlists.map(p => this.getOldPlaylist(p))
|
|
}
|
|
|
|
/**
|
|
* 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 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
|
|
}
|
|
}
|
|
|
|
Playlist.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
|
|
})
|
|
}
|
|
|
|
}
|
|
})
|
|
|
|
return Playlist
|
|
} |