mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-03-24 00:16:39 +01:00
Updates to migrate off of old library model
This commit is contained in:
parent
5d13faef33
commit
159ccd807f
@ -507,15 +507,13 @@ class LibraryController {
|
||||
payload.offset = payload.page * payload.limit
|
||||
|
||||
// TODO: Temporary way of handling collapse sub-series. Either remove feature or handle through sql queries
|
||||
// TODO: Update to new library model
|
||||
const oldLibrary = Database.libraryModel.getOldLibrary(req.library)
|
||||
const filterByGroup = payload.filterBy?.split('.').shift()
|
||||
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, oldLibrary)
|
||||
payload.results = await libraryHelpers.handleCollapseSubseries(payload, seriesId, req.user, req.library)
|
||||
} else {
|
||||
const { libraryItems, count } = await Database.libraryItemModel.getByFilterAndSort(oldLibrary, req.user, payload)
|
||||
const { libraryItems, count } = await Database.libraryItemModel.getByFilterAndSort(req.library, req.user, payload)
|
||||
payload.results = libraryItems
|
||||
payload.total = count
|
||||
}
|
||||
@ -589,9 +587,6 @@ class LibraryController {
|
||||
* @param {Response} res
|
||||
*/
|
||||
async getAllSeriesForLibrary(req, res) {
|
||||
// TODO: Update to new library model
|
||||
const oldLibrary = Database.libraryModel.getOldLibrary(req.library)
|
||||
|
||||
const include = (req.query.include || '')
|
||||
.split(',')
|
||||
.map((v) => v.trim().toLowerCase())
|
||||
@ -610,7 +605,7 @@ class LibraryController {
|
||||
}
|
||||
|
||||
const offset = payload.page * payload.limit
|
||||
const { series, count } = await seriesFilters.getFilteredSeries(oldLibrary, req.user, payload.filterBy, payload.sortBy, payload.sortDesc, include, payload.limit, offset)
|
||||
const { series, count } = await seriesFilters.getFilteredSeries(req.library, req.user, payload.filterBy, payload.sortBy, payload.sortDesc, include, payload.limit, offset)
|
||||
|
||||
payload.total = count
|
||||
payload.results = series
|
||||
@ -741,14 +736,12 @@ class LibraryController {
|
||||
* @param {Response} res
|
||||
*/
|
||||
async getUserPersonalizedShelves(req, res) {
|
||||
// TODO: Update to new library model
|
||||
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(oldLibrary, req.user, include, limitPerShelf)
|
||||
const shelves = await Database.libraryItemModel.getPersonalizedShelves(req.library, req.user, include, limitPerShelf)
|
||||
res.json(shelves)
|
||||
}
|
||||
|
||||
@ -1098,8 +1091,7 @@ class LibraryController {
|
||||
if (req.library.mediaType !== 'podcast') {
|
||||
return res.sendStatus(404)
|
||||
}
|
||||
// TODO: Update to new library model
|
||||
const oldLibrary = Database.libraryModel.getOldLibrary(req.library)
|
||||
|
||||
const payload = {
|
||||
episodes: [],
|
||||
limit: req.query.limit && !isNaN(req.query.limit) ? Number(req.query.limit) : 0,
|
||||
@ -1107,7 +1099,7 @@ class LibraryController {
|
||||
}
|
||||
|
||||
const offset = payload.page * payload.limit
|
||||
payload.episodes = await libraryItemsPodcastFilters.getRecentEpisodes(req.user, oldLibrary, payload.limit, offset)
|
||||
payload.episodes = await libraryItemsPodcastFilters.getRecentEpisodes(req.user, req.library, payload.limit, offset)
|
||||
res.json(payload)
|
||||
}
|
||||
|
||||
|
@ -44,11 +44,11 @@ class MiscController {
|
||||
const files = Object.values(req.files)
|
||||
const { title, author, series, folder: folderId, library: libraryId } = req.body
|
||||
|
||||
const library = await Database.libraryModel.getOldById(libraryId)
|
||||
const library = await Database.libraryModel.findByPk(libraryId)
|
||||
if (!library) {
|
||||
return res.status(404).send(`Library not found with id ${libraryId}`)
|
||||
}
|
||||
const folder = library.folders.find((fold) => fold.id === folderId)
|
||||
const folder = library.libraryFolders.find((fold) => fold.id === folderId)
|
||||
if (!folder) {
|
||||
return res.status(404).send(`Folder not found with id ${folderId} in library ${library.name}`)
|
||||
}
|
||||
@ -63,7 +63,7 @@ class MiscController {
|
||||
// before sanitizing all the directory parts to remove illegal chars and finally prepending
|
||||
// the base folder path
|
||||
const cleanedOutputDirectoryParts = outputDirectoryParts.filter(Boolean).map((part) => sanitizeFilename(part))
|
||||
const outputDirectory = Path.join(...[folder.fullPath, ...cleanedOutputDirectoryParts])
|
||||
const outputDirectory = Path.join(...[folder.path, ...cleanedOutputDirectoryParts])
|
||||
|
||||
await fs.ensureDir(outputDirectory)
|
||||
|
||||
|
@ -38,13 +38,13 @@ class PodcastController {
|
||||
}
|
||||
const payload = req.body
|
||||
|
||||
const library = await Database.libraryModel.getOldById(payload.libraryId)
|
||||
const library = await Database.libraryModel.findByPk(payload.libraryId)
|
||||
if (!library) {
|
||||
Logger.error(`[PodcastController] Create: Library not found "${payload.libraryId}"`)
|
||||
return res.status(404).send('Library not found')
|
||||
}
|
||||
|
||||
const folder = library.folders.find((fold) => fold.id === payload.folderId)
|
||||
const folder = library.libraryFolders.find((fold) => fold.id === payload.folderId)
|
||||
if (!folder) {
|
||||
Logger.error(`[PodcastController] Create: Folder not found "${payload.folderId}"`)
|
||||
return res.status(404).send('Folder not found')
|
||||
|
@ -23,7 +23,7 @@ class NotificationManager {
|
||||
}
|
||||
|
||||
Logger.debug(`[NotificationManager] onPodcastEpisodeDownloaded: Episode "${episode.title}" for podcast ${libraryItem.media.metadata.title}`)
|
||||
const library = await Database.libraryModel.getOldById(libraryItem.libraryId)
|
||||
const library = await Database.libraryModel.findByPk(libraryItem.libraryId)
|
||||
const eventData = {
|
||||
libraryItemId: libraryItem.id,
|
||||
libraryId: libraryItem.libraryId,
|
||||
|
@ -11,8 +11,6 @@ const LibraryFile = require('../objects/files/LibraryFile')
|
||||
const Book = require('./Book')
|
||||
const Podcast = require('./Podcast')
|
||||
|
||||
const ShareManager = require('../managers/ShareManager')
|
||||
|
||||
/**
|
||||
* @typedef LibraryFileObject
|
||||
* @property {string} ino
|
||||
@ -559,14 +557,14 @@ class LibraryItem extends Model {
|
||||
|
||||
/**
|
||||
* Get library items using filter and sort
|
||||
* @param {oldLibrary} library
|
||||
* @param {import('./Library')} library
|
||||
* @param {import('./User')} user
|
||||
* @param {object} options
|
||||
* @returns {{ libraryItems:oldLibraryItem[], count:number }}
|
||||
*/
|
||||
static async getByFilterAndSort(library, user, options) {
|
||||
let start = Date.now()
|
||||
const { libraryItems, count } = await libraryFilters.getFilteredLibraryItems(library, user, options)
|
||||
const { libraryItems, count } = await libraryFilters.getFilteredLibraryItems(library.id, user, options)
|
||||
Logger.debug(`Loaded ${libraryItems.length} of ${count} items for libary page in ${((Date.now() - start) / 1000).toFixed(2)}s`)
|
||||
|
||||
return {
|
||||
@ -602,7 +600,7 @@ class LibraryItem extends Model {
|
||||
|
||||
/**
|
||||
* Get home page data personalized shelves
|
||||
* @param {oldLibrary} library
|
||||
* @param {import('./Library')} library
|
||||
* @param {import('./User')} user
|
||||
* @param {string[]} include
|
||||
* @param {number} limit
|
||||
|
@ -26,27 +26,27 @@ class LibraryScanner {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} libraryId
|
||||
* @param {string} libraryId
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isLibraryScanning(libraryId) {
|
||||
return this.librariesScanning.some(ls => ls.id === libraryId)
|
||||
return this.librariesScanning.some((ls) => ls.id === libraryId)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} libraryId
|
||||
*
|
||||
* @param {string} libraryId
|
||||
*/
|
||||
setCancelLibraryScan(libraryId) {
|
||||
const libraryScanning = this.librariesScanning.find(ls => ls.id === libraryId)
|
||||
const libraryScanning = this.librariesScanning.find((ls) => ls.id === libraryId)
|
||||
if (!libraryScanning) return
|
||||
this.cancelLibraryScan[libraryId] = true
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import('../objects/Library')} library
|
||||
* @param {boolean} [forceRescan]
|
||||
*
|
||||
* @param {import('../objects/Library')} library
|
||||
* @param {boolean} [forceRescan]
|
||||
*/
|
||||
async scan(library, forceRescan = false) {
|
||||
if (this.isLibraryScanning(library.id)) {
|
||||
@ -89,7 +89,7 @@ class LibraryScanner {
|
||||
libraryScan.setComplete()
|
||||
|
||||
Logger.info(`[LibraryScanner] Library scan ${libraryScan.id} completed in ${libraryScan.elapsedTimestamp} | ${libraryScan.resultStats}`)
|
||||
this.librariesScanning = this.librariesScanning.filter(ls => ls.id !== library.id)
|
||||
this.librariesScanning = this.librariesScanning.filter((ls) => ls.id !== library.id)
|
||||
|
||||
if (canceled && !libraryScan.totalResults) {
|
||||
task.setFinished('Scan canceled')
|
||||
@ -116,8 +116,8 @@ class LibraryScanner {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import('./LibraryScan')} libraryScan
|
||||
*
|
||||
* @param {import('./LibraryScan')} libraryScan
|
||||
* @param {boolean} forceRescan
|
||||
* @returns {Promise<boolean>} true if scan canceled
|
||||
*/
|
||||
@ -151,14 +151,10 @@ class LibraryScanner {
|
||||
let oldLibraryItemsUpdated = []
|
||||
for (const existingLibraryItem of existingLibraryItems) {
|
||||
// First try to find matching library item with exact file path
|
||||
let libraryItemData = libraryItemDataFound.find(lid => lid.path === existingLibraryItem.path)
|
||||
let libraryItemData = libraryItemDataFound.find((lid) => lid.path === existingLibraryItem.path)
|
||||
if (!libraryItemData) {
|
||||
// Fallback to finding matching library item with matching inode value
|
||||
libraryItemData = libraryItemDataFound.find(lid =>
|
||||
ItemToItemInoMatch(lid, existingLibraryItem) ||
|
||||
ItemToFileInoMatch(lid, existingLibraryItem) ||
|
||||
ItemToFileInoMatch(existingLibraryItem, lid)
|
||||
)
|
||||
libraryItemData = libraryItemDataFound.find((lid) => ItemToItemInoMatch(lid, existingLibraryItem) || ItemToFileInoMatch(lid, existingLibraryItem) || ItemToFileInoMatch(existingLibraryItem, lid))
|
||||
if (libraryItemData) {
|
||||
libraryScan.addLog(LogLevel.INFO, `Library item with path "${existingLibraryItem.path}" was not found, but library item inode "${existingLibraryItem.ino}" was found at path "${libraryItemData.path}"`)
|
||||
}
|
||||
@ -166,7 +162,7 @@ class LibraryScanner {
|
||||
|
||||
if (!libraryItemData) {
|
||||
// Podcast folder can have no episodes and still be valid
|
||||
if (libraryScan.libraryMediaType === 'podcast' && await fs.pathExists(existingLibraryItem.path)) {
|
||||
if (libraryScan.libraryMediaType === 'podcast' && (await fs.pathExists(existingLibraryItem.path))) {
|
||||
libraryScan.addLog(LogLevel.INFO, `Library item "${existingLibraryItem.relPath}" folder exists but has no episodes`)
|
||||
} else {
|
||||
libraryScan.addLog(LogLevel.WARN, `Library Item "${existingLibraryItem.path}" (inode: ${existingLibraryItem.ino}) is missing`)
|
||||
@ -184,7 +180,7 @@ class LibraryScanner {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
libraryItemDataFound = libraryItemDataFound.filter(lidf => lidf !== libraryItemData)
|
||||
libraryItemDataFound = libraryItemDataFound.filter((lidf) => lidf !== libraryItemData)
|
||||
let libraryItemDataUpdated = await libraryItemData.checkLibraryItemData(existingLibraryItem, libraryScan)
|
||||
if (libraryItemDataUpdated || forceRescan) {
|
||||
if (forceRescan || libraryItemData.hasLibraryFileChanges || libraryItemData.hasPathChange) {
|
||||
@ -210,7 +206,10 @@ class LibraryScanner {
|
||||
// Emit item updates in chunks of 10 to client
|
||||
if (oldLibraryItemsUpdated.length === 10) {
|
||||
// TODO: Should only emit to clients where library item is accessible
|
||||
SocketAuthority.emitter('items_updated', oldLibraryItemsUpdated.map(li => li.toJSONExpanded()))
|
||||
SocketAuthority.emitter(
|
||||
'items_updated',
|
||||
oldLibraryItemsUpdated.map((li) => li.toJSONExpanded())
|
||||
)
|
||||
oldLibraryItemsUpdated = []
|
||||
}
|
||||
|
||||
@ -219,7 +218,10 @@ class LibraryScanner {
|
||||
// Emit item updates to client
|
||||
if (oldLibraryItemsUpdated.length) {
|
||||
// TODO: Should only emit to clients where library item is accessible
|
||||
SocketAuthority.emitter('items_updated', oldLibraryItemsUpdated.map(li => li.toJSONExpanded()))
|
||||
SocketAuthority.emitter(
|
||||
'items_updated',
|
||||
oldLibraryItemsUpdated.map((li) => li.toJSONExpanded())
|
||||
)
|
||||
}
|
||||
|
||||
// Authors and series that were removed from books should be removed if they are now empty
|
||||
@ -228,15 +230,18 @@ class LibraryScanner {
|
||||
// Update missing library items
|
||||
if (libraryItemIdsMissing.length) {
|
||||
libraryScan.addLog(LogLevel.INFO, `Updating ${libraryItemIdsMissing.length} library items missing`)
|
||||
await Database.libraryItemModel.update({
|
||||
isMissing: true,
|
||||
lastScan: Date.now(),
|
||||
lastScanVersion: packageJson.version
|
||||
}, {
|
||||
where: {
|
||||
id: libraryItemIdsMissing
|
||||
await Database.libraryItemModel.update(
|
||||
{
|
||||
isMissing: true,
|
||||
lastScan: Date.now(),
|
||||
lastScanVersion: packageJson.version
|
||||
},
|
||||
{
|
||||
where: {
|
||||
id: libraryItemIdsMissing
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (this.cancelLibraryScan[libraryScan.libraryId]) return true
|
||||
@ -256,7 +261,10 @@ class LibraryScanner {
|
||||
// Emit new items in chunks of 10 to client
|
||||
if (newOldLibraryItems.length === 10) {
|
||||
// TODO: Should only emit to clients where library item is accessible
|
||||
SocketAuthority.emitter('items_added', newOldLibraryItems.map(li => li.toJSONExpanded()))
|
||||
SocketAuthority.emitter(
|
||||
'items_added',
|
||||
newOldLibraryItems.map((li) => li.toJSONExpanded())
|
||||
)
|
||||
newOldLibraryItems = []
|
||||
}
|
||||
|
||||
@ -265,15 +273,18 @@ class LibraryScanner {
|
||||
// Emit new items to client
|
||||
if (newOldLibraryItems.length) {
|
||||
// TODO: Should only emit to clients where library item is accessible
|
||||
SocketAuthority.emitter('items_added', newOldLibraryItems.map(li => li.toJSONExpanded()))
|
||||
SocketAuthority.emitter(
|
||||
'items_added',
|
||||
newOldLibraryItems.map((li) => li.toJSONExpanded())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get scan data for library folder
|
||||
* @param {import('../objects/Library')} library
|
||||
* @param {import('../objects/Folder')} folder
|
||||
* @param {import('../objects/Library')} library
|
||||
* @param {import('../objects/Folder')} folder
|
||||
* @returns {LibraryItemScanData[]}
|
||||
*/
|
||||
async scanFolder(library, folder) {
|
||||
@ -321,27 +332,29 @@ class LibraryScanner {
|
||||
continue
|
||||
}
|
||||
|
||||
items.push(new LibraryItemScanData({
|
||||
libraryFolderId: folder.id,
|
||||
libraryId: folder.libraryId,
|
||||
mediaType: library.mediaType,
|
||||
ino: libraryItemFolderStats.ino,
|
||||
mtimeMs: libraryItemFolderStats.mtimeMs || 0,
|
||||
ctimeMs: libraryItemFolderStats.ctimeMs || 0,
|
||||
birthtimeMs: libraryItemFolderStats.birthtimeMs || 0,
|
||||
path: libraryItemData.path,
|
||||
relPath: libraryItemData.relPath,
|
||||
isFile,
|
||||
mediaMetadata: libraryItemData.mediaMetadata || null,
|
||||
libraryFiles: fileObjs
|
||||
}))
|
||||
items.push(
|
||||
new LibraryItemScanData({
|
||||
libraryFolderId: folder.id,
|
||||
libraryId: folder.libraryId,
|
||||
mediaType: library.mediaType,
|
||||
ino: libraryItemFolderStats.ino,
|
||||
mtimeMs: libraryItemFolderStats.mtimeMs || 0,
|
||||
ctimeMs: libraryItemFolderStats.ctimeMs || 0,
|
||||
birthtimeMs: libraryItemFolderStats.birthtimeMs || 0,
|
||||
path: libraryItemData.path,
|
||||
relPath: libraryItemData.relPath,
|
||||
isFile,
|
||||
mediaMetadata: libraryItemData.mediaMetadata || null,
|
||||
libraryFiles: fileObjs
|
||||
})
|
||||
)
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan files changed from Watcher
|
||||
* @param {import('../Watcher').PendingFileUpdate[]} fileUpdates
|
||||
* @param {import('../Watcher').PendingFileUpdate[]} fileUpdates
|
||||
* @param {Task} pendingTask
|
||||
*/
|
||||
async scanFilesChanged(fileUpdates, pendingTask) {
|
||||
@ -366,7 +379,7 @@ class LibraryScanner {
|
||||
|
||||
for (const folderId in folderGroups) {
|
||||
const libraryId = folderGroups[folderId].libraryId
|
||||
// const library = await Database.libraryModel.getOldById(libraryId)
|
||||
|
||||
const library = await Database.libraryModel.findByPk(libraryId, {
|
||||
include: {
|
||||
model: Database.libraryFolderModel,
|
||||
@ -381,7 +394,7 @@ class LibraryScanner {
|
||||
}
|
||||
const folder = library.libraryFolders[0]
|
||||
|
||||
const relFilePaths = folderGroups[folderId].fileUpdates.map(fileUpdate => fileUpdate.relPath)
|
||||
const relFilePaths = folderGroups[folderId].fileUpdates.map((fileUpdate) => fileUpdate.relPath)
|
||||
const fileUpdateGroup = scanUtils.groupFilesIntoLibraryItemPaths(library.mediaType, relFilePaths)
|
||||
|
||||
if (!Object.keys(fileUpdateGroup).length) {
|
||||
@ -432,7 +445,7 @@ class LibraryScanner {
|
||||
|
||||
/**
|
||||
* Group array of PendingFileUpdate from Watcher by folder
|
||||
* @param {import('../Watcher').PendingFileUpdate[]} fileUpdates
|
||||
* @param {import('../Watcher').PendingFileUpdate[]} fileUpdates
|
||||
* @returns {Record<string,{libraryId:string, folderId:string, fileUpdates:import('../Watcher').PendingFileUpdate[]}>}
|
||||
*/
|
||||
getFileUpdatesGrouped(fileUpdates) {
|
||||
@ -453,9 +466,9 @@ class LibraryScanner {
|
||||
|
||||
/**
|
||||
* Scan grouped paths for library folder coming from Watcher
|
||||
* @param {import('../models/Library')} library
|
||||
* @param {import('../models/LibraryFolder')} folder
|
||||
* @param {Record<string, string[]>} fileUpdateGroup
|
||||
* @param {import('../models/Library')} library
|
||||
* @param {import('../models/LibraryFolder')} folder
|
||||
* @param {Record<string, string[]>} fileUpdateGroup
|
||||
* @returns {Promise<Record<string,number>>}
|
||||
*/
|
||||
async scanFolderUpdates(library, folder, fileUpdateGroup) {
|
||||
@ -471,7 +484,7 @@ class LibraryScanner {
|
||||
for (const itemDir in updateGroup) {
|
||||
if (isSingleMediaFile(fileUpdateGroup, itemDir)) continue // Media in root path
|
||||
|
||||
const itemDirNestedFiles = fileUpdateGroup[itemDir].filter(b => b.includes('/'))
|
||||
const itemDirNestedFiles = fileUpdateGroup[itemDir].filter((b) => b.includes('/'))
|
||||
if (!itemDirNestedFiles.length) continue
|
||||
|
||||
const firstNest = itemDirNestedFiles[0].split('/').shift()
|
||||
@ -523,7 +536,15 @@ class LibraryScanner {
|
||||
|
||||
const potentialChildDirs = [fullPath]
|
||||
for (let i = 0; i < itemDirParts.length; i++) {
|
||||
potentialChildDirs.push(Path.posix.join(fileUtils.filePathToPOSIX(folder.path), itemDir.split('/').slice(0, -1 - i).join('/')))
|
||||
potentialChildDirs.push(
|
||||
Path.posix.join(
|
||||
fileUtils.filePathToPOSIX(folder.path),
|
||||
itemDir
|
||||
.split('/')
|
||||
.slice(0, -1 - i)
|
||||
.join('/')
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Check if book dir group is already an item
|
||||
@ -535,10 +556,7 @@ class LibraryScanner {
|
||||
let updatedLibraryItemDetails = {}
|
||||
if (!existingLibraryItem) {
|
||||
const isSingleMedia = isSingleMediaFile(fileUpdateGroup, itemDir)
|
||||
existingLibraryItem =
|
||||
await findLibraryItemByItemToItemInoMatch(library.id, fullPath) ||
|
||||
await findLibraryItemByItemToFileInoMatch(library.id, fullPath, isSingleMedia) ||
|
||||
await findLibraryItemByFileToItemInoMatch(library.id, fullPath, isSingleMedia, fileUpdateGroup[itemDir])
|
||||
existingLibraryItem = (await findLibraryItemByItemToItemInoMatch(library.id, fullPath)) || (await findLibraryItemByItemToFileInoMatch(library.id, fullPath, isSingleMedia)) || (await findLibraryItemByFileToItemInoMatch(library.id, fullPath, isSingleMedia, fileUpdateGroup[itemDir]))
|
||||
if (existingLibraryItem) {
|
||||
// Update library item paths for scan
|
||||
existingLibraryItem.path = fullPath
|
||||
@ -603,7 +621,7 @@ class LibraryScanner {
|
||||
module.exports = new LibraryScanner()
|
||||
|
||||
function ItemToFileInoMatch(libraryItem1, libraryItem2) {
|
||||
return libraryItem1.isFile && libraryItem2.libraryFiles.some(lf => lf.ino === libraryItem1.ino)
|
||||
return libraryItem1.isFile && libraryItem2.libraryFiles.some((lf) => lf.ino === libraryItem1.ino)
|
||||
}
|
||||
|
||||
function ItemToItemInoMatch(libraryItem1, libraryItem2) {
|
||||
@ -611,9 +629,7 @@ function ItemToItemInoMatch(libraryItem1, libraryItem2) {
|
||||
}
|
||||
|
||||
function hasAudioFiles(fileUpdateGroup, itemDir) {
|
||||
return isSingleMediaFile(fileUpdateGroup, itemDir) ?
|
||||
scanUtils.checkFilepathIsAudioFile(fileUpdateGroup[itemDir]) :
|
||||
fileUpdateGroup[itemDir].some(scanUtils.checkFilepathIsAudioFile)
|
||||
return isSingleMediaFile(fileUpdateGroup, itemDir) ? scanUtils.checkFilepathIsAudioFile(fileUpdateGroup[itemDir]) : fileUpdateGroup[itemDir].some(scanUtils.checkFilepathIsAudioFile)
|
||||
}
|
||||
|
||||
function isSingleMediaFile(fileUpdateGroup, itemDir) {
|
||||
@ -627,8 +643,7 @@ async function findLibraryItemByItemToItemInoMatch(libraryId, fullPath) {
|
||||
libraryId: libraryId,
|
||||
ino: ino
|
||||
})
|
||||
if (existingLibraryItem)
|
||||
Logger.debug(`[LibraryScanner] Found library item with matching inode "${ino}" at path "${existingLibraryItem.path}"`)
|
||||
if (existingLibraryItem) Logger.debug(`[LibraryScanner] Found library item with matching inode "${ino}" at path "${existingLibraryItem.path}"`)
|
||||
return existingLibraryItem
|
||||
}
|
||||
|
||||
@ -637,18 +652,20 @@ async function findLibraryItemByItemToFileInoMatch(libraryId, fullPath, isSingle
|
||||
// check if it was moved from another folder by comparing the ino to the library files
|
||||
const ino = await fileUtils.getIno(fullPath)
|
||||
if (!ino) return null
|
||||
const existingLibraryItem = await Database.libraryItemModel.findOneOld([
|
||||
const existingLibraryItem = await Database.libraryItemModel.findOneOld(
|
||||
[
|
||||
{
|
||||
libraryId: libraryId
|
||||
},
|
||||
sequelize.where(sequelize.literal('(SELECT count(*) FROM json_each(libraryFiles) WHERE json_valid(json_each.value) AND json_each.value->>"$.ino" = :inode)'), {
|
||||
[sequelize.Op.gt]: 0
|
||||
})
|
||||
],
|
||||
{
|
||||
libraryId: libraryId
|
||||
},
|
||||
sequelize.where(sequelize.literal('(SELECT count(*) FROM json_each(libraryFiles) WHERE json_valid(json_each.value) AND json_each.value->>"$.ino" = :inode)'), {
|
||||
[sequelize.Op.gt]: 0
|
||||
})
|
||||
], {
|
||||
inode: ino
|
||||
})
|
||||
if (existingLibraryItem)
|
||||
Logger.debug(`[LibraryScanner] Found library item with a library file matching inode "${ino}" at path "${existingLibraryItem.path}"`)
|
||||
inode: ino
|
||||
}
|
||||
)
|
||||
if (existingLibraryItem) Logger.debug(`[LibraryScanner] Found library item with a library file matching inode "${ino}" at path "${existingLibraryItem.path}"`)
|
||||
return existingLibraryItem
|
||||
}
|
||||
|
||||
@ -667,7 +684,6 @@ async function findLibraryItemByFileToItemInoMatch(libraryId, fullPath, isSingle
|
||||
[sequelize.Op.in]: itemFileInos
|
||||
}
|
||||
})
|
||||
if (existingLibraryItem)
|
||||
Logger.debug(`[LibraryScanner] Found library item with inode matching one of "${itemFileInos.join(',')}" at path "${existingLibraryItem.path}"`)
|
||||
if (existingLibraryItem) Logger.debug(`[LibraryScanner] Found library item with inode matching one of "${itemFileInos.join(',')}" at path "${existingLibraryItem.path}"`)
|
||||
return existingLibraryItem
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ module.exports = {
|
||||
* @param {*} payload
|
||||
* @param {string} seriesId
|
||||
* @param {import('../models/User')} user
|
||||
* @param {import('../objects/Library')} library
|
||||
* @param {import('../models/Library')} library
|
||||
* @returns {Object[]}
|
||||
*/
|
||||
async handleCollapseSubseries(payload, seriesId, user, library) {
|
||||
|
@ -15,12 +15,12 @@ module.exports = {
|
||||
|
||||
/**
|
||||
* Get library items using filter and sort
|
||||
* @param {import('../../objects/Library')} library
|
||||
* @param {string} libraryId
|
||||
* @param {import('../../models/User')} user
|
||||
* @param {object} options
|
||||
* @returns {object} { libraryItems:LibraryItem[], count:number }
|
||||
*/
|
||||
async getFilteredLibraryItems(library, user, options) {
|
||||
async getFilteredLibraryItems(libraryId, user, options) {
|
||||
const { filterBy, sortBy, sortDesc, limit, offset, collapseseries, include, mediaType } = options
|
||||
|
||||
let filterValue = null
|
||||
@ -33,22 +33,22 @@ module.exports = {
|
||||
}
|
||||
|
||||
if (mediaType === 'book') {
|
||||
return libraryItemsBookFilters.getFilteredLibraryItems(library.id, user, filterGroup, filterValue, sortBy, sortDesc, collapseseries, include, limit, offset)
|
||||
return libraryItemsBookFilters.getFilteredLibraryItems(libraryId, user, filterGroup, filterValue, sortBy, sortDesc, collapseseries, include, limit, offset)
|
||||
} else {
|
||||
return libraryItemsPodcastFilters.getFilteredLibraryItems(library.id, user, filterGroup, filterValue, sortBy, sortDesc, include, limit, offset)
|
||||
return libraryItemsPodcastFilters.getFilteredLibraryItems(libraryId, user, filterGroup, filterValue, sortBy, sortDesc, include, limit, offset)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get library items for continue listening & continue reading shelves
|
||||
* @param {import('../../objects/Library')} library
|
||||
* @param {import('../../models/Library')} library
|
||||
* @param {import('../../models/User')} user
|
||||
* @param {string[]} include
|
||||
* @param {number} limit
|
||||
* @returns {Promise<{ items:import('../../models/LibraryItem')[], count:number }>}
|
||||
*/
|
||||
async getMediaItemsInProgress(library, user, include, limit) {
|
||||
if (library.mediaType === 'book') {
|
||||
if (library.isBook) {
|
||||
const { libraryItems, count } = await libraryItemsBookFilters.getFilteredLibraryItems(library.id, user, 'progress', 'in-progress', 'progress', true, false, include, limit, 0, true)
|
||||
return {
|
||||
items: libraryItems.map((li) => {
|
||||
@ -78,14 +78,14 @@ module.exports = {
|
||||
|
||||
/**
|
||||
* Get library items for most recently added shelf
|
||||
* @param {import('../../objects/Library')} library
|
||||
* @param {import('../../models/Library')} library
|
||||
* @param {import('../../models/User')} user
|
||||
* @param {string[]} include
|
||||
* @param {number} limit
|
||||
* @returns {object} { libraryItems:LibraryItem[], count:number }
|
||||
*/
|
||||
async getLibraryItemsMostRecentlyAdded(library, user, include, limit) {
|
||||
if (library.mediaType === 'book') {
|
||||
if (library.isBook) {
|
||||
const { libraryItems, count } = await libraryItemsBookFilters.getFilteredLibraryItems(library.id, user, 'recent', null, 'addedAt', true, false, include, limit, 0)
|
||||
return {
|
||||
libraryItems: libraryItems.map((li) => {
|
||||
@ -126,7 +126,7 @@ module.exports = {
|
||||
|
||||
/**
|
||||
* Get library items for continue series shelf
|
||||
* @param {import('../../objects/Library')} library
|
||||
* @param {import('../../models/Library')} library
|
||||
* @param {import('../../models/User')} user
|
||||
* @param {string[]} include
|
||||
* @param {number} limit
|
||||
@ -154,14 +154,15 @@ module.exports = {
|
||||
|
||||
/**
|
||||
* Get library items or podcast episodes for the "Listen Again" and "Read Again" shelf
|
||||
* @param {import('../../objects/Library')} library
|
||||
*
|
||||
* @param {import('../../models/Library')} library
|
||||
* @param {import('../../models/User')} user
|
||||
* @param {string[]} include
|
||||
* @param {number} limit
|
||||
* @returns {object} { items:object[], count:number }
|
||||
* @returns {Promise<{ items:oldLibraryItem[], count:number }>}
|
||||
*/
|
||||
async getMediaFinished(library, user, include, limit) {
|
||||
if (library.mediaType === 'book') {
|
||||
if (library.isBook) {
|
||||
const { libraryItems, count } = await libraryItemsBookFilters.getFilteredLibraryItems(library.id, user, 'progress', 'finished', 'progress', true, false, include, limit, 0)
|
||||
return {
|
||||
items: libraryItems.map((li) => {
|
||||
@ -191,7 +192,7 @@ module.exports = {
|
||||
|
||||
/**
|
||||
* Get series for recent series shelf
|
||||
* @param {import('../../objects/Library')} library
|
||||
* @param {import('../../models/Library')} library
|
||||
* @param {import('../../models/User')} user
|
||||
* @param {string[]} include
|
||||
* @param {number} limit
|
||||
@ -316,10 +317,11 @@ module.exports = {
|
||||
/**
|
||||
* Get most recently created authors for "Newest Authors" shelf
|
||||
* Author must be linked to at least 1 book
|
||||
* @param {oldLibrary} library
|
||||
*
|
||||
* @param {import('../../models/Library')} library
|
||||
* @param {import('../../models/User')} user
|
||||
* @param {number} limit
|
||||
* @returns {object} { authors:oldAuthor[], count:number }
|
||||
* @returns {Promise<{ authors:oldAuthor[], count:number }>}
|
||||
*/
|
||||
async getNewestAuthors(library, user, limit) {
|
||||
if (library.mediaType !== 'book') return { authors: [], count: 0 }
|
||||
@ -359,11 +361,11 @@ module.exports = {
|
||||
|
||||
/**
|
||||
* Get book library items for the "Discover" shelf
|
||||
* @param {oldLibrary} library
|
||||
* @param {import('../../models/Library')} library
|
||||
* @param {import('../../models/User')} user
|
||||
* @param {string[]} include
|
||||
* @param {number} limit
|
||||
* @returns {object} {libraryItems:oldLibraryItem[], count:number}
|
||||
* @returns {Promise<{libraryItems:oldLibraryItem[], count:number}>}
|
||||
*/
|
||||
async getLibraryItemsToDiscover(library, user, include, limit) {
|
||||
if (library.mediaType !== 'book') return { libraryItems: [], count: 0 }
|
||||
@ -386,10 +388,10 @@ module.exports = {
|
||||
|
||||
/**
|
||||
* Get podcast episodes most recently added
|
||||
* @param {oldLibrary} library
|
||||
* @param {import('../../models/Library')} library
|
||||
* @param {import('../../models/User')} user
|
||||
* @param {number} limit
|
||||
* @returns {object} {libraryItems:oldLibraryItem[], count:number}
|
||||
* @returns {Promise<{libraryItems:oldLibraryItem[], count:number}>}
|
||||
*/
|
||||
async getNewestPodcastEpisodes(library, user, limit) {
|
||||
if (library.mediaType !== 'podcast') return { libraryItems: [], count: 0 }
|
||||
@ -411,7 +413,7 @@ module.exports = {
|
||||
* @param {import('../../models/User')} user
|
||||
* @param {number} limit
|
||||
* @param {number} offset
|
||||
* @returns {Promise<object>} { libraryItems:LibraryItem[], count:number }
|
||||
* @returns {Promise<{ libraryItems:import('../../objects/LibraryItem')[], count:number }>}
|
||||
*/
|
||||
async getLibraryItemsForAuthor(author, user, limit, offset) {
|
||||
const { libraryItems, count } = await libraryItemsBookFilters.getFilteredLibraryItems(author.libraryId, user, 'authors', author.id, 'addedAt', true, false, [], limit, offset)
|
||||
@ -424,7 +426,7 @@ module.exports = {
|
||||
/**
|
||||
* Get book library items in a collection
|
||||
* @param {oldCollection} collection
|
||||
* @returns {Promise<LibraryItem[]>}
|
||||
* @returns {Promise<import('../../models/LibraryItem')[]>}
|
||||
*/
|
||||
getLibraryItemsForCollection(collection) {
|
||||
return libraryItemsBookFilters.getLibraryItemsForCollection(collection)
|
||||
|
@ -636,7 +636,7 @@ module.exports = {
|
||||
* 2. Has no books in progress
|
||||
* 3. Has at least 1 unfinished book
|
||||
* TODO: Reduce queries
|
||||
* @param {import('../../objects/Library')} library
|
||||
* @param {import('../../models/Library')} library
|
||||
* @param {import('../../models/User')} user
|
||||
* @param {string[]} include
|
||||
* @param {number} limit
|
||||
@ -911,7 +911,7 @@ module.exports = {
|
||||
/**
|
||||
* Get book library items in a collection
|
||||
* @param {oldCollection} collection
|
||||
* @returns {Promise<LibraryItem[]>}
|
||||
* @returns {Promise<import('../../models/LibraryItem')[]>}
|
||||
*/
|
||||
async getLibraryItemsForCollection(collection) {
|
||||
if (!collection?.books?.length) {
|
||||
|
@ -412,12 +412,12 @@ module.exports = {
|
||||
/**
|
||||
* Most recent podcast episodes not finished
|
||||
* @param {import('../../models/User')} user
|
||||
* @param {import('../../objects/Library')} oldLibrary
|
||||
* @param {import('../../models/Library')} library
|
||||
* @param {number} limit
|
||||
* @param {number} offset
|
||||
* @returns {Promise<object[]>}
|
||||
*/
|
||||
async getRecentEpisodes(user, oldLibrary, limit, offset) {
|
||||
async getRecentEpisodes(user, library, limit, offset) {
|
||||
const userPermissionPodcastWhere = this.getUserPermissionPodcastWhereQuery(user)
|
||||
|
||||
const episodes = await Database.podcastEpisodeModel.findAll({
|
||||
@ -435,7 +435,7 @@ module.exports = {
|
||||
include: {
|
||||
model: Database.libraryItemModel,
|
||||
where: {
|
||||
libraryId: oldLibrary.id
|
||||
libraryId: library.id
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -11,7 +11,7 @@ module.exports = {
|
||||
/**
|
||||
* Get series filtered and sorted
|
||||
*
|
||||
* @param {import('../../objects/Library')} library
|
||||
* @param {import('../../models/Library')} library
|
||||
* @param {import('../../models/User')} user
|
||||
* @param {string} filterBy
|
||||
* @param {string} sortBy
|
||||
|
Loading…
Reference in New Issue
Block a user