mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-01-08 00:08:14 +01:00
Update:Load playlists only when needed & remove podcast episode from playlist when deleted
This commit is contained in:
parent
5a9eed0a5a
commit
710a62c2af
@ -19,7 +19,6 @@ class Database {
|
||||
// TODO: below data should be loaded from the DB as needed
|
||||
this.libraryItems = []
|
||||
this.settings = []
|
||||
this.playlists = []
|
||||
this.authors = []
|
||||
this.series = []
|
||||
|
||||
@ -160,9 +159,6 @@ class Database {
|
||||
this.libraryItems = await this.models.libraryItem.loadAllLibraryItems()
|
||||
Logger.info(`[Database] Loaded ${this.libraryItems.length} library items`)
|
||||
|
||||
this.playlists = await this.models.playlist.getOldPlaylists()
|
||||
Logger.info(`[Database] Loaded ${this.playlists.length} playlists`)
|
||||
|
||||
this.authors = await this.models.author.getOldAuthors()
|
||||
Logger.info(`[Database] Loaded ${this.authors.length} authors`)
|
||||
|
||||
@ -341,7 +337,6 @@ class Database {
|
||||
await this.createBulkPlaylistMediaItems(playlistMediaItems)
|
||||
}
|
||||
}
|
||||
this.playlists.push(oldPlaylist)
|
||||
}
|
||||
|
||||
updatePlaylist(oldPlaylist) {
|
||||
@ -364,7 +359,6 @@ class Database {
|
||||
async removePlaylist(playlistId) {
|
||||
if (!this.sequelize) return false
|
||||
await this.models.playlist.removeById(playlistId)
|
||||
this.playlists = this.playlists.filter(p => p.id !== playlistId)
|
||||
}
|
||||
|
||||
createPlaylistMediaItem(playlistMediaItem) {
|
||||
|
@ -83,7 +83,7 @@ class LibraryController {
|
||||
return res.json({
|
||||
filterdata: libraryHelpers.getDistinctFilterDataNew(req.libraryItems),
|
||||
issues: req.libraryItems.filter(li => li.hasIssues).length,
|
||||
numUserPlaylists: Database.playlists.filter(p => p.userId === req.user.id && p.libraryId === req.library.id).length,
|
||||
numUserPlaylists: await Database.models.playlist.getNumPlaylistsForUserAndLibrary(req.user.id, req.library.id),
|
||||
library: req.library
|
||||
})
|
||||
}
|
||||
@ -557,7 +557,8 @@ class LibraryController {
|
||||
|
||||
// api/libraries/:id/playlists
|
||||
async getUserPlaylistsForLibrary(req, res) {
|
||||
let playlistsForUser = Database.playlists.filter(p => p.userId === req.user.id && p.libraryId === req.library.id).map(p => p.toJSONExpanded(Database.libraryItems))
|
||||
let playlistsForUser = await Database.models.playlist.getPlaylistsForUserAndLibrary(req.user.id, req.library.id)
|
||||
playlistsForUser = playlistsForUser.map(p => p.toJSONExpanded(Database.libraryItems))
|
||||
|
||||
const payload = {
|
||||
results: [],
|
||||
|
@ -22,9 +22,10 @@ class PlaylistController {
|
||||
}
|
||||
|
||||
// GET: api/playlists
|
||||
findAllForUser(req, res) {
|
||||
async findAllForUser(req, res) {
|
||||
const playlistsForUser = await Database.models.playlist.getPlaylistsForUserAndLibrary(req.user.id)
|
||||
res.json({
|
||||
playlists: Database.playlists.filter(p => p.userId === req.user.id).map(p => p.toJSONExpanded(Database.libraryItems))
|
||||
playlists: playlistsForUser.map(p => p.toJSONExpanded(Database.libraryItems))
|
||||
})
|
||||
}
|
||||
|
||||
@ -231,9 +232,9 @@ class PlaylistController {
|
||||
res.json(jsonExpanded)
|
||||
}
|
||||
|
||||
middleware(req, res, next) {
|
||||
async middleware(req, res, next) {
|
||||
if (req.params.id) {
|
||||
const playlist = Database.playlists.find(p => p.id === req.params.id)
|
||||
const playlist = await Database.models.playlist.getById(req.params.id)
|
||||
if (!playlist) {
|
||||
return res.status(404).send('Playlist not found')
|
||||
}
|
||||
|
@ -241,18 +241,18 @@ class PodcastController {
|
||||
|
||||
// DELETE: api/podcasts/:id/episode/:episodeId
|
||||
async removeEpisode(req, res) {
|
||||
var episodeId = req.params.episodeId
|
||||
var libraryItem = req.libraryItem
|
||||
var hardDelete = req.query.hard === '1'
|
||||
const episodeId = req.params.episodeId
|
||||
const libraryItem = req.libraryItem
|
||||
const hardDelete = req.query.hard === '1'
|
||||
|
||||
var episode = libraryItem.media.episodes.find(ep => ep.id === episodeId)
|
||||
const episode = libraryItem.media.episodes.find(ep => ep.id === episodeId)
|
||||
if (!episode) {
|
||||
Logger.error(`[PodcastController] removeEpisode episode ${episodeId} not found for item ${libraryItem.id}`)
|
||||
return res.sendStatus(404)
|
||||
}
|
||||
|
||||
if (hardDelete) {
|
||||
var audioFile = episode.audioFile
|
||||
const audioFile = episode.audioFile
|
||||
// TODO: this will trigger the watcher. should maybe handle this gracefully
|
||||
await fs.remove(audioFile.metadata.path).then(() => {
|
||||
Logger.info(`[PodcastController] Hard deleted episode file at "${audioFile.metadata.path}"`)
|
||||
@ -267,6 +267,22 @@ class PodcastController {
|
||||
libraryItem.removeLibraryFile(episodeRemoved.audioFile.ino)
|
||||
}
|
||||
|
||||
// Update/remove playlists that had this podcast episode
|
||||
const playlistsWithEpisode = await Database.models.playlist.getPlaylistsForMediaItemIds([episodeId])
|
||||
for (const playlist of playlistsWithEpisode) {
|
||||
playlist.removeItem(libraryItem.id, episodeId)
|
||||
|
||||
// If playlist is now empty then remove it
|
||||
if (!playlist.items.length) {
|
||||
Logger.info(`[PodcastController] Playlist "${playlist.name}" has no more items - removing it`)
|
||||
await Database.removePlaylist(playlist.id)
|
||||
SocketAuthority.clientEmitter(playlist.userId, 'playlist_removed', playlist.toJSONExpanded(Database.libraryItems))
|
||||
} else {
|
||||
await Database.updatePlaylist(playlist)
|
||||
SocketAuthority.clientEmitter(playlist.userId, 'playlist_updated', playlist.toJSONExpanded(Database.libraryItems))
|
||||
}
|
||||
}
|
||||
|
||||
await Database.updateLibraryItem(libraryItem)
|
||||
SocketAuthority.emitter('item_updated', libraryItem.toJSONExpanded())
|
||||
res.json(libraryItem.toJSON())
|
||||
|
@ -122,7 +122,7 @@ class UserController {
|
||||
// Todo: check if user is logged in and cancel streams
|
||||
|
||||
// Remove user playlists
|
||||
const userPlaylists = Database.playlists.filter(p => p.userId === user.id)
|
||||
const userPlaylists = await Database.models.playlist.getPlaylistsForUserAndLibrary(user.id)
|
||||
for (const playlist of userPlaylists) {
|
||||
await Database.removePlaylist(playlist.id)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
const { DataTypes, Model } = require('sequelize')
|
||||
const { DataTypes, Model, Op } = require('sequelize')
|
||||
const Logger = require('../Logger')
|
||||
|
||||
const oldPlaylist = require('../objects/Playlist')
|
||||
@ -119,6 +119,146 @@ module.exports = (sequelize) => {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<oldPlaylist[]>}
|
||||
*/
|
||||
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']]
|
||||
})
|
||||
return playlistMediaItemsExpanded.map(pmie => {
|
||||
pmie.playlist.playlistMediaItems = pmie.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
|
||||
})
|
||||
|
||||
return this.getOldPlaylist(pmie.playlist)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Playlist.init({
|
||||
|
@ -389,7 +389,7 @@ class ApiRouter {
|
||||
}
|
||||
|
||||
// TODO: Remove open sessions for library item
|
||||
|
||||
let mediaItemIds = []
|
||||
if (libraryItem.isBook) {
|
||||
// remove book from collections
|
||||
const collectionsWithBook = await Database.models.collection.getAllForBook(libraryItem.media.id)
|
||||
@ -401,12 +401,15 @@ class ApiRouter {
|
||||
|
||||
// Check remove empty series
|
||||
await this.checkRemoveEmptySeries(libraryItem.media.metadata.series, libraryItem.id)
|
||||
|
||||
mediaItemIds.push(libraryItem.media.id)
|
||||
} else if (libraryItem.isPodcast) {
|
||||
mediaItemIds.push(...libraryItem.media.episodes.map(ep => ep.id))
|
||||
}
|
||||
|
||||
// remove item from playlists
|
||||
const playlistsWithItem = Database.playlists.filter(p => p.hasItemsForLibraryItem(libraryItem.id))
|
||||
for (let i = 0; i < playlistsWithItem.length; i++) {
|
||||
const playlist = playlistsWithItem[i]
|
||||
const playlistsWithItem = await Database.models.playlist.getPlaylistsForMediaItemIds(mediaItemIds)
|
||||
for (const playlist of playlistsWithItem) {
|
||||
playlist.removeItemsForLibraryItem(libraryItem.id)
|
||||
|
||||
// If playlist is now empty then remove it
|
||||
|
Loading…
Reference in New Issue
Block a user