diff --git a/server/controllers/LibraryController.js b/server/controllers/LibraryController.js index 1d2d5bfa..f9aeba3f 100644 --- a/server/controllers/LibraryController.js +++ b/server/controllers/LibraryController.js @@ -1441,29 +1441,27 @@ class LibraryController { const itemIds = req.query.ids.split(',') const libraryItems = await Database.libraryItemModel.findAll({ - attributes: ['id', 'libraryId', 'path'], + attributes: ['id', 'libraryId', 'path', 'isFile'], where: { - id: itemIds, - libraryId: req.params.id, - mediaType: 'book' + id: itemIds } }) - Logger.info(`[LibraryItemController] User "${req.user.username}" requested download for items "${itemIds}"`) + Logger.info(`[LibraryController] User "${req.user.username}" requested download for items "${itemIds}"`) const filename = `LibraryItems-${Date.now()}.zip` - const libraryItemPaths = libraryItems.map((li) => li.path) + const pathObjects = libraryItems.map((li) => ({ path: li.path, isFile: li.isFile })) - if (!libraryItemPaths.length) { - Logger.warn(`[LibraryItemController] No library items found for ids "${itemIds}"`) + if (!pathObjects.length) { + Logger.warn(`[LibraryController] No library items found for ids "${itemIds}"`) return res.status(404).send('Library items not found') } try { - await zipHelpers.zipDirectoriesPipe(libraryItemPaths, filename, res) - Logger.info(`[LibraryItemController] Downloaded item "${filename}" at "${libraryItemPaths}"`) + await zipHelpers.zipDirectoriesPipe(pathObjects, filename, res) + Logger.info(`[LibraryController] Downloaded ${pathObjects.length} items "${filename}"`) } catch (error) { - Logger.error(`[LibraryItemController] Download failed for item "${filename}" at "${libraryItemPaths}"`, error) + Logger.error(`[LibraryController] Download failed for items "${filename}" at ${pathObjects.map((po) => po.path).join(', ')}`, error) zipHelpers.handleDownloadError(error, res) } } diff --git a/server/utils/zipHelpers.js b/server/utils/zipHelpers.js index 6849df5d..421ca73c 100644 --- a/server/utils/zipHelpers.js +++ b/server/utils/zipHelpers.js @@ -1,6 +1,7 @@ +const Path = require('path') +const { Response } = require('express') const Logger = require('../Logger') const archiver = require('../libs/archiver') -const { lstatSync } = require('node:fs') module.exports.zipDirectoryPipe = (path, filename, res) => { return new Promise((resolve, reject) => { @@ -55,12 +56,12 @@ module.exports.zipDirectoryPipe = (path, filename, res) => { /** * Creates a zip archive containing multiple directories and streams it to the response. * - * @param {string[]} paths - Array of directory paths to include in the zip archive. + * @param {{ path: string, isFile: boolean }[]} pathObjects * @param {string} filename - Name of the zip file to be sent as attachment. - * @param {object} res - Response object to pipe the archive data to. + * @param {Response} res - Response object to pipe the archive data to. * @returns {Promise} - Promise that resolves when the zip operation completes. */ -module.exports.zipDirectoriesPipe = (paths, filename, res) => { +module.exports.zipDirectoriesPipe = (pathObjects, filename, res) => { return new Promise((resolve, reject) => { // create a file to stream archive data to res.attachment(filename) @@ -105,20 +106,14 @@ module.exports.zipDirectoriesPipe = (paths, filename, res) => { archive.pipe(res) // Add each path as a directory in the zip - paths.forEach(path => { - - const paths = path.split('/') - - // Check if path is file or directory - if (lstatSync(path).isDirectory()) { - const dirName = path.split('/').pop() - + pathObjects.forEach((pathObject) => { + if (!pathObject.isFile) { // Add the directory to the archive with its name as the root folder - archive.directory(path, dirName); + archive.directory(pathObject.path, Path.basename(pathObject.path)) } else { - archive.file(path, { name: paths[paths.length - 1] }); + archive.file(pathObject.path, { name: Path.basename(pathObject.path) }) } - }); + }) archive.finalize() }) @@ -127,8 +122,8 @@ module.exports.zipDirectoriesPipe = (paths, filename, res) => { /** * Handles errors that occur during the download process. * - * @param error - * @param res + * @param {*} error + * @param {Response} res * @returns {*} */ module.exports.handleDownloadError = (error, res) => {