Updates to use new Library model

This commit is contained in:
advplyr 2024-08-23 16:59:51 -05:00
parent 8774e6be71
commit 38edcdca4b
6 changed files with 114 additions and 57 deletions

View File

@ -384,11 +384,6 @@ class Database {
return Promise.all(oldBooks.map((oldBook) => this.models.book.saveFromOld(oldBook)))
}
updateLibrary(oldLibrary) {
if (!this.sequelize) return false
return this.models.library.updateFromOld(oldLibrary)
}
removeLibrary(libraryId) {
if (!this.sequelize) return false
return this.models.library.removeById(libraryId)

View File

@ -142,7 +142,7 @@ class Server {
await this.backupManager.init()
await this.rssFeedManager.init()
const libraries = await Database.libraryModel.getAllOldLibraries()
const libraries = await Database.libraryModel.getAllWithFolders()
await this.cronManager.init(libraries)
this.apiCacheManager.init()

View File

@ -45,6 +45,10 @@ class FolderWatcher extends EventEmitter {
return this.pendingFileUpdates.map((f) => f.path)
}
/**
*
* @param {import('./models/Library')} library
*/
buildLibraryWatcher(library) {
if (this.libraryWatchers.find((w) => w.id === library.id)) {
Logger.warn('[Watcher] Already watching library', library.name)
@ -52,7 +56,7 @@ class FolderWatcher extends EventEmitter {
}
Logger.info(`[Watcher] Initializing watcher for "${library.name}".`)
const folderPaths = library.folderPaths
const folderPaths = library.libraryFolders.map((f) => f.path)
folderPaths.forEach((fp) => {
Logger.debug(`[Watcher] Init watcher for library folder path "${fp}"`)
})
@ -90,12 +94,16 @@ class FolderWatcher extends EventEmitter {
this.libraryWatchers.push({
id: library.id,
name: library.name,
folders: library.folders,
paths: library.folderPaths,
libraryFolders: library.libraryFolders,
paths: folderPaths,
watcher
})
}
/**
*
* @param {import('./models/Library')[]} libraries
*/
initWatcher(libraries) {
libraries.forEach((lib) => {
if (!lib.settings.disableWatcher) {
@ -104,12 +112,17 @@ class FolderWatcher extends EventEmitter {
})
}
/**
*
* @param {import('./models/Library')} library
*/
addLibrary(library) {
if (this.disabled || library.settings.disableWatcher) return
this.buildLibraryWatcher(library)
}
/**
* TODO: Update to new library model
*
* @param {import('./objects/Library')} library
*/
@ -129,8 +142,9 @@ class FolderWatcher extends EventEmitter {
libwatcher.name = library.name
// If any folder paths were added or removed then re-init watcher
const pathsToAdd = library.folderPaths.filter((path) => !libwatcher.paths.includes(path))
const pathsRemoved = libwatcher.paths.filter((path) => !library.folderPaths.includes(path))
const folderPaths = library.libraryFolders.map((f) => f.path)
const pathsToAdd = folderPaths.filter((path) => !libwatcher.paths.includes(path))
const pathsRemoved = libwatcher.paths.filter((path) => !folderPaths.includes(path))
if (pathsToAdd.length || pathsRemoved.length) {
Logger.info(`[Watcher] Re-Initializing watcher for "${library.name}".`)
@ -145,6 +159,10 @@ class FolderWatcher extends EventEmitter {
}
}
/**
*
* @param {import('./models/Library')} library
*/
removeLibrary(library) {
if (this.disabled) return
var libwatcher = this.libraryWatchers.find((lib) => lib.id === library.id)
@ -255,15 +273,15 @@ class FolderWatcher extends EventEmitter {
}
// Get file folder
const folder = libwatcher.folders.find((fold) => isSameOrSubPath(fold.fullPath, path))
const folder = libwatcher.libraryFolders.find((fold) => isSameOrSubPath(fold.path, path))
if (!folder) {
Logger.error(`[Watcher] New file folder not found in library "${libwatcher.name}" with path "${path}"`)
return
}
const folderFullPath = filePathToPOSIX(folder.fullPath)
const folderPath = filePathToPOSIX(folder.path)
const relPath = path.replace(folderFullPath, '')
const relPath = path.replace(folderPath, '')
if (Path.extname(relPath).toLowerCase() === '.part') {
Logger.debug(`[Watcher] Ignoring .part file "${relPath}"`)

View File

@ -4,7 +4,6 @@ const Path = require('path')
const fs = require('../libs/fsExtra')
const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority')
const Library = require('../objects/Library')
const libraryHelpers = require('../utils/libraryHelpers')
const libraryItemsBookFilters = require('../utils/queries/libraryItemsBookFilters')
const libraryItemFilters = require('../utils/queries/libraryItemFilters')
@ -158,7 +157,7 @@ class LibraryController {
SocketAuthority.emitter('library_added', oldLibrary.toJSON(), userFilter)
// Add library watcher
this.watcher.addLibrary(oldLibrary)
this.watcher.addLibrary(library)
res.json(oldLibrary)
}
@ -190,15 +189,16 @@ class LibraryController {
const filterdata = await libraryFilters.getFilterData(req.library.mediaType, req.library.id)
const customMetadataProviders = await Database.customMetadataProviderModel.getForClientByMediaType(req.library.mediaType)
const oldLibrary = Database.libraryModel.getOldLibrary(req.library)
return res.json({
filterdata,
issues: filterdata.numIssues,
numUserPlaylists: await Database.playlistModel.getNumPlaylistsForUserAndLibrary(req.user.id, req.library.id),
customMetadataProviders,
library: req.library
library: oldLibrary
})
}
res.json(req.library)
res.json(oldLibrary)
}
/**
@ -220,7 +220,7 @@ class LibraryController {
*/
async update(req, res) {
/** @type {import('../objects/Library')} */
const library = req.library
const oldLibrary = Database.libraryModel.getOldLibrary(req.library)
// Validate that the custom provider exists if given any
if (req.body.provider?.startsWith('custom-')) {
@ -259,7 +259,7 @@ class LibraryController {
}
// Handle removing folders
for (const folder of library.folders) {
for (const folder of oldLibrary.folders) {
if (!req.body.folders.some((f) => f.id === folder.id)) {
// Remove library items in folder
const libraryItemsInFolder = await Database.libraryItemModel.findAll({
@ -278,10 +278,10 @@ class LibraryController {
}
]
})
Logger.info(`[LibraryController] Removed folder "${folder.fullPath}" from library "${library.name}" with ${libraryItemsInFolder.length} library items`)
Logger.info(`[LibraryController] Removed folder "${folder.fullPath}" from library "${oldLibrary.name}" with ${libraryItemsInFolder.length} library items`)
for (const libraryItem of libraryItemsInFolder) {
let mediaItemIds = []
if (library.isPodcast) {
if (oldLibrary.isPodcast) {
mediaItemIds = libraryItem.media.podcastEpisodes.map((pe) => pe.id)
} else {
mediaItemIds.push(libraryItem.mediaId)
@ -293,26 +293,27 @@ class LibraryController {
}
}
const hasUpdates = library.update(req.body)
const hasUpdates = oldLibrary.update(req.body)
// TODO: Should check if this is an update to folder paths or name only
if (hasUpdates) {
// Update watcher
this.watcher.updateLibrary(library)
// Update auto scan cron
this.cronManager.updateLibraryScanCron(library)
this.cronManager.updateLibraryScanCron(oldLibrary)
await Database.updateLibrary(library)
const updatedLibrary = await Database.libraryModel.updateFromOld(oldLibrary)
updatedLibrary.libraryFolders = await updatedLibrary.getLibraryFolders()
// Update watcher
this.watcher.updateLibrary(updatedLibrary)
// Only emit to users with access to library
const userFilter = (user) => {
return user.checkCanAccessLibrary?.(library.id)
return user.checkCanAccessLibrary?.(oldLibrary.id)
}
SocketAuthority.emitter('library_updated', library.toJSON(), userFilter)
SocketAuthority.emitter('library_updated', oldLibrary.toJSON(), userFilter)
await Database.resetLibraryIssuesFilterData(library.id)
await Database.resetLibraryIssuesFilterData(oldLibrary.id)
}
return res.json(library.toJSON())
return res.json(oldLibrary.toJSON())
}
/**
@ -323,10 +324,10 @@ class LibraryController {
* @param {Response} res
*/
async delete(req, res) {
const library = req.library
const library = Database.libraryModel.getOldLibrary(req.library)
// Remove library watcher
this.watcher.removeLibrary(library)
this.watcher.removeLibrary(req.library)
// Remove collections for library
const numCollectionsRemoved = await Database.collectionModel.removeAllForLibrary(library.id)
@ -386,6 +387,8 @@ class LibraryController {
* @param {Response} res
*/
async getLibraryItems(req, res) {
const oldLibrary = Database.libraryModel.getOldLibrary(req.library)
const include = (req.query.include || '')
.split(',')
.map((v) => v.trim().toLowerCase())
@ -411,9 +414,9 @@ class LibraryController {
const filterByValue = filterByGroup ? libraryFilters.decode(payload.filterBy.replace(`${filterByGroup}.`, '')) : null
if (filterByGroup === 'series' && filterByValue !== 'no-series' && payload.collapseseries) {
const seriesId = libraryFilters.decode(payload.filterBy.split('.')[1])
payload.results = await libraryHelpers.handleCollapseSubseries(payload, seriesId, req.user, req.library)
payload.results = await libraryHelpers.handleCollapseSubseries(payload, seriesId, req.user, oldLibrary)
} else {
const { libraryItems, count } = await Database.libraryItemModel.getByFilterAndSort(req.library, req.user, payload)
const { libraryItems, count } = await Database.libraryItemModel.getByFilterAndSort(oldLibrary, req.user, payload)
payload.results = libraryItems
payload.total = count
}
@ -461,7 +464,7 @@ class LibraryController {
Logger.info(`[LibraryController] Removing ${libraryItemsWithIssues.length} items with issues`)
for (const libraryItem of libraryItemsWithIssues) {
let mediaItemIds = []
if (req.library.isPodcast) {
if (req.library.mediaType === 'podcast') {
mediaItemIds = libraryItem.media.podcastEpisodes.map((pe) => pe.id)
} else {
mediaItemIds.push(libraryItem.mediaId)
@ -486,6 +489,8 @@ class LibraryController {
* @param {Response} res
*/
async getAllSeriesForLibrary(req, res) {
const oldLibrary = Database.libraryModel.getOldLibrary(req.library)
const include = (req.query.include || '')
.split(',')
.map((v) => v.trim().toLowerCase())
@ -504,7 +509,7 @@ class LibraryController {
}
const offset = payload.page * payload.limit
const { series, count } = await seriesFilters.getFilteredSeries(req.library, req.user, payload.filterBy, payload.sortBy, payload.sortDesc, include, payload.limit, offset)
const { series, count } = await seriesFilters.getFilteredSeries(oldLibrary, req.user, payload.filterBy, payload.sortBy, payload.sortDesc, include, payload.limit, offset)
payload.total = count
payload.results = series
@ -635,12 +640,13 @@ class LibraryController {
* @param {Response} res
*/
async getUserPersonalizedShelves(req, res) {
const oldLibrary = Database.libraryModel.getOldLibrary(req.library)
const limitPerShelf = req.query.limit && !isNaN(req.query.limit) ? Number(req.query.limit) || 10 : 10
const include = (req.query.include || '')
.split(',')
.map((v) => v.trim().toLowerCase())
.filter((v) => !!v)
const shelves = await Database.libraryItemModel.getPersonalizedShelves(req.library, req.user, include, limitPerShelf)
const shelves = await Database.libraryItemModel.getPersonalizedShelves(oldLibrary, req.user, include, limitPerShelf)
res.json(shelves)
}
@ -668,7 +674,7 @@ class LibraryController {
}
if (library.update({ displayOrder: orderdata[i].newOrder })) {
hasUpdates = true
await Database.updateLibrary(library)
await Database.libraryModel.updateFromOld(library)
}
}
@ -696,10 +702,11 @@ class LibraryController {
if (!req.query.q || typeof req.query.q !== 'string') {
return res.status(400).send('Invalid request. Query param "q" must be a string')
}
const oldLibrary = Database.libraryModel.getOldLibrary(req.library)
const limit = req.query.limit && !isNaN(req.query.limit) ? Number(req.query.limit) : 12
const query = asciiOnlyToLowerCase(req.query.q.trim())
const matches = await libraryItemFilters.search(req.user, req.library, query, limit)
const matches = await libraryItemFilters.search(req.user, oldLibrary, query, limit)
res.json(matches)
}
@ -715,7 +722,7 @@ class LibraryController {
largestItems: await libraryItemFilters.getLargestItems(req.library.id, 10)
}
if (req.library.isBook) {
if (req.library.mediaType === 'book') {
const authors = await authorFilters.getAuthorsWithCount(req.library.id, 10)
const genres = await libraryItemsBookFilters.getGenresWithCount(req.library.id)
const bookStats = await libraryItemsBookFilters.getBookLibraryStats(req.library.id)
@ -938,7 +945,8 @@ class LibraryController {
Logger.error(`[LibraryController] Non-root user "${req.user.username}" attempted to match library items`)
return res.sendStatus(403)
}
Scanner.matchLibraryItems(req.library)
const oldLibrary = Database.libraryModel.getOldLibrary(req.library)
Scanner.matchLibraryItems(oldLibrary)
res.sendStatus(200)
}
@ -956,9 +964,9 @@ class LibraryController {
return res.sendStatus(403)
}
res.sendStatus(200)
const oldLibrary = Database.libraryModel.getOldLibrary(req.library)
const forceRescan = req.query.force === '1'
await LibraryScanner.scan(req.library, forceRescan)
await LibraryScanner.scan(oldLibrary, forceRescan)
await Database.resetLibraryIssuesFilterData(req.library.id)
Logger.info('[LibraryController] Scan complete')
@ -972,10 +980,10 @@ class LibraryController {
* @param {Response} res
*/
async getRecentEpisodes(req, res) {
if (!req.library.isPodcast) {
if (req.library.mediaType !== 'podcast') {
return res.sendStatus(404)
}
const oldLibrary = Database.libraryModel.getOldLibrary(req.library)
const payload = {
episodes: [],
limit: req.query.limit && !isNaN(req.query.limit) ? Number(req.query.limit) : 0,
@ -983,7 +991,7 @@ class LibraryController {
}
const offset = payload.page * payload.limit
payload.episodes = await libraryItemsPodcastFilters.getRecentEpisodes(req.user, req.library, payload.limit, offset)
payload.episodes = await libraryItemsPodcastFilters.getRecentEpisodes(req.user, oldLibrary, payload.limit, offset)
res.json(payload)
}
@ -1076,7 +1084,8 @@ class LibraryController {
return res.sendStatus(403)
}
const library = await Database.libraryModel.getOldById(req.params.id)
// const library = await Database.libraryModel.getOldById(req.params.id)
const library = await Database.libraryModel.findByIdWithFolders(req.params.id)
if (!library) {
return res.status(404).send('Library not found')
}

View File

@ -21,7 +21,8 @@ class CronManager {
/**
* Initialize library scan crons & podcast download crons
* @param {import('../objects/Library')[]} libraries
*
* @param {import('../models/Library')[]} libraries
*/
async init(libraries) {
this.initOpenSessionCleanupCron()
@ -46,7 +47,7 @@ class CronManager {
/**
* Initialize library scan crons
* @param {import('../objects/Library')[]} libraries
* @param {import('../models/Library')[]} libraries
*/
initLibraryScanCrons(libraries) {
for (const library of libraries) {
@ -59,17 +60,17 @@ class CronManager {
/**
* Start cron schedule for library
*
* @param {import('../objects/Library')} _library
* @param {import('../models/Library')} _library
*/
startCronForLibrary(_library) {
Logger.debug(`[CronManager] Init library scan cron for ${_library.name} on schedule ${_library.settings.autoScanCronExpression}`)
const libScanCron = cron.schedule(_library.settings.autoScanCronExpression, async () => {
const library = await Database.libraryModel.getOldById(_library.id)
if (!library) {
const oldLibrary = await Database.libraryModel.getOldById(_library.id)
if (!oldLibrary) {
Logger.error(`[CronManager] Library not found for scan cron ${_library.id}`)
} else {
Logger.debug(`[CronManager] Library scan cron executing for ${library.name}`)
LibraryScanner.scan(library)
Logger.debug(`[CronManager] Library scan cron executing for ${oldLibrary.name}`)
LibraryScanner.scan(oldLibrary)
}
})
this.libraryScanCrons.push({
@ -79,11 +80,21 @@ class CronManager {
})
}
/**
* TODO: Update to new library model
*
* @param {*} library
*/
removeCronForLibrary(library) {
Logger.debug(`[CronManager] Removing library scan cron for ${library.name}`)
this.libraryScanCrons = this.libraryScanCrons.filter((lsc) => lsc.libraryId !== library.id)
}
/**
* TODO: Update to new library model
*
* @param {*} library
*/
updateLibraryScanCron(library) {
const expression = library.settings.autoScanCronExpression
const existingCron = this.libraryScanCrons.find((lsc) => lsc.libraryId === library.id)

View File

@ -43,6 +43,8 @@ class Library extends Model {
this.createdAt
/** @type {Date} */
this.updatedAt
/** @type {import('./LibraryFolder')[]|undefined} */
this.libraryFolders
}
/**
@ -74,6 +76,28 @@ class Library extends Model {
}
}
/**
*
* @returns {Promise<Library[]>}
*/
static getAllWithFolders() {
return this.findAll({
include: this.sequelize.models.libraryFolder,
order: [['displayOrder', 'ASC']]
})
}
/**
*
* @param {string} libraryId
* @returns {Promise<Library>}
*/
static findByIdWithFolders(libraryId) {
return this.findByPk(libraryId, {
include: this.sequelize.models.libraryFolder
})
}
/**
* Get all old libraries
* @returns {Promise<oldLibrary[]>}
@ -121,7 +145,7 @@ class Library extends Model {
/**
* Update library and library folders
* @param {object} oldLibrary
* @returns
* @returns {Promise<Library|null>}
*/
static async updateFromOld(oldLibrary) {
const existingLibrary = await this.findByPk(oldLibrary.id, {