From e16d3d72b69cb3ed547643cb7ae8f822327966cd Mon Sep 17 00:00:00 2001 From: advplyr Date: Mon, 24 Mar 2025 18:01:38 -0500 Subject: [PATCH] Fix uploader check if item already exists in a subdirectory #4146 --- client/pages/upload/index.vue | 8 +++- client/strings/en-us.json | 2 + server/controllers/FileSystemController.js | 45 ++++++++++++++++++++-- 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/client/pages/upload/index.vue b/client/pages/upload/index.vue index 7fd74b78..39955181 100644 --- a/client/pages/upload/index.vue +++ b/client/pages/upload/index.vue @@ -362,10 +362,14 @@ export default { for (const item of items) { const filepath = Path.join(this.selectedFolder.fullPath, item.directory) const exists = await this.$axios - .$post(`/api/filesystem/pathexists`, { filepath }) + .$post(`/api/filesystem/pathexists`, { filepath, directory: item.directory, folderPath: this.selectedFolder.fullPath }) .then((data) => { if (data.exists) { - this.$toast.error(`Filepath "${filepath}" already exists on server`) + if (data.libraryItemTitle) { + this.$toast.error(this.$getString('ToastUploaderItemExistsInSubdirectoryError', [data.libraryItemTitle])) + } else { + this.$toast.error(this.$getString('ToastUploaderFilepathExistsError', [filepath])) + } } return data.exists }) diff --git a/client/strings/en-us.json b/client/strings/en-us.json index c67c1630..636e3019 100644 --- a/client/strings/en-us.json +++ b/client/strings/en-us.json @@ -1086,6 +1086,8 @@ "ToastUnknownError": "Unknown error", "ToastUnlinkOpenIdFailed": "Failed to unlink user from OpenID", "ToastUnlinkOpenIdSuccess": "User unlinked from OpenID", + "ToastUploaderFilepathExistsError": "Filepath \"{0}\" already exists on server", + "ToastUploaderItemExistsInSubdirectoryError": "Item \"{0}\" is using a subdirectory of the upload path.", "ToastUserDeleteFailed": "Failed to delete user", "ToastUserDeleteSuccess": "User deleted", "ToastUserPasswordChangeSuccess": "Password changed successfully", diff --git a/server/controllers/FileSystemController.js b/server/controllers/FileSystemController.js index e923c495..d0b190a4 100644 --- a/server/controllers/FileSystemController.js +++ b/server/controllers/FileSystemController.js @@ -4,6 +4,7 @@ const Logger = require('../Logger') const fs = require('../libs/fsExtra') const { toNumber } = require('../utils/index') const fileUtils = require('../utils/fileUtils') +const Database = require('../Database') /** * @typedef RequestUserObject @@ -87,14 +88,50 @@ class FileSystemController { return res.sendStatus(403) } - const filepath = req.body.filepath - if (!filepath?.length) { + const { filepath, directory, folderPath } = req.body + + if (!filepath?.length || typeof filepath !== 'string') { return res.sendStatus(400) } const exists = await fs.pathExists(filepath) - res.json({ - exists + + if (exists) { + return res.json({ + exists: true + }) + } + + // If directory and folderPath are passed in, check if a library item exists in a subdirectory + // See: https://github.com/advplyr/audiobookshelf/issues/4146 + if (typeof directory === 'string' && typeof folderPath === 'string' && directory.length > 0 && folderPath.length > 0) { + const cleanedDirectory = directory.split('/').filter(Boolean).join('/') + if (cleanedDirectory.includes('/')) { + // Can only be 2 levels deep + const possiblePaths = [] + const subdir = Path.dirname(directory) + possiblePaths.push(fileUtils.filePathToPOSIX(Path.join(folderPath, subdir))) + if (subdir.includes('/')) { + possiblePaths.push(fileUtils.filePathToPOSIX(Path.join(folderPath, Path.dirname(subdir)))) + } + + const libraryItem = await Database.libraryItemModel.findOne({ + where: { + path: possiblePaths + } + }) + + if (libraryItem) { + return res.json({ + exists: true, + libraryItemTitle: libraryItem.title + }) + } + } + } + + return res.json({ + exists: false }) } }