Merge branch 'advplyr:master' into master

This commit is contained in:
Usama Khalil 2025-05-28 13:15:48 +03:00 committed by GitHub
commit ed4af72515
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 61 additions and 37 deletions

View File

@ -276,6 +276,11 @@ export default {
text: this.$strings.ButtonIssues, text: this.$strings.ButtonIssues,
value: 'issues', value: 'issues',
sublist: false sublist: false
},
{
text: this.$strings.LabelRSSFeedOpen,
value: 'feed-open',
sublist: false
} }
] ]
}, },

View File

@ -162,7 +162,7 @@ export default {
} else { } else {
// Find closest bitrate rounding up // Find closest bitrate rounding up
const bitratesToMatch = [32, 64, 128, 192] const bitratesToMatch = [32, 64, 128, 192]
const closestBitrate = bitratesToMatch.find((bitrate) => bitrate >= this.currentBitrate) const closestBitrate = bitratesToMatch.find((bitrate) => bitrate >= this.currentBitrate) || 192
this.selectedBitrate = closestBitrate + 'k' this.selectedBitrate = closestBitrate + 'k'
} }

View File

@ -359,15 +359,14 @@ export default {
// Check if path already exists before starting upload // Check if path already exists before starting upload
// uploading fails if path already exists // uploading fails if path already exists
for (const item of items) { for (const item of items) {
const filepath = Path.join(this.selectedFolder.fullPath, item.directory)
const exists = await this.$axios const exists = await this.$axios
.$post(`/api/filesystem/pathexists`, { filepath, directory: item.directory, folderPath: this.selectedFolder.fullPath }) .$post(`/api/filesystem/pathexists`, { directory: item.directory, folderPath: this.selectedFolder.fullPath })
.then((data) => { .then((data) => {
if (data.exists) { if (data.exists) {
if (data.libraryItemTitle) { if (data.libraryItemTitle) {
this.$toast.error(this.$getString('ToastUploaderItemExistsInSubdirectoryError', [data.libraryItemTitle])) this.$toast.error(this.$getString('ToastUploaderItemExistsInSubdirectoryError', [data.libraryItemTitle]))
} else { } else {
this.$toast.error(this.$getString('ToastUploaderFilepathExistsError', [filepath])) this.$toast.error(this.$getString('ToastUploaderFilepathExistsError', [Path.join(this.selectedFolder.fullPath, item.directory)]))
} }
} }
return data.exists return data.exists

View File

@ -84,49 +84,67 @@ class FileSystemController {
*/ */
async checkPathExists(req, res) { async checkPathExists(req, res) {
if (!req.user.canUpload) { if (!req.user.canUpload) {
Logger.error(`[FileSystemController] Non-admin user "${req.user.username}" attempting to check path exists`) Logger.error(`[FileSystemController] User "${req.user.username}" without upload permissions attempting to check path exists`)
return res.sendStatus(403) return res.sendStatus(403)
} }
const { filepath, directory, folderPath } = req.body const { directory, folderPath } = req.body
if (!filepath?.length || typeof filepath !== 'string') { if (!directory?.length || typeof directory !== 'string' || !folderPath?.length || typeof folderPath !== 'string') {
Logger.error(`[FileSystemController] Invalid request body: ${JSON.stringify(req.body)}`)
return res.status(400).json({
error: 'Invalid request body'
})
}
// Check that library folder exists
const libraryFolder = await Database.libraryFolderModel.findOne({
where: {
path: folderPath
}
})
if (!libraryFolder) {
Logger.error(`[FileSystemController] Library folder not found: ${folderPath}`)
return res.sendStatus(404)
}
const filepath = Path.posix.join(libraryFolder.path, directory)
// Ensure filepath is inside library folder (prevents directory traversal)
if (!filepath.startsWith(libraryFolder.path)) {
Logger.error(`[FileSystemController] Filepath is not inside library folder: ${filepath}`)
return res.sendStatus(400) return res.sendStatus(400)
} }
const exists = await fs.pathExists(filepath) if (await fs.pathExists(filepath)) {
if (exists) {
return res.json({ return res.json({
exists: true exists: true
}) })
} }
// If directory and folderPath are passed in, check if a library item exists in a subdirectory // Check if a library item exists in a subdirectory
// See: https://github.com/advplyr/audiobookshelf/issues/4146 // 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('/')
const cleanedDirectory = directory.split('/').filter(Boolean).join('/') if (cleanedDirectory.includes('/')) {
if (cleanedDirectory.includes('/')) { // Can only be 2 levels deep
// Can only be 2 levels deep const possiblePaths = []
const possiblePaths = [] const subdir = Path.dirname(directory)
const subdir = Path.dirname(directory) possiblePaths.push(fileUtils.filePathToPOSIX(Path.join(folderPath, subdir)))
possiblePaths.push(fileUtils.filePathToPOSIX(Path.join(folderPath, subdir))) if (subdir.includes('/')) {
if (subdir.includes('/')) { possiblePaths.push(fileUtils.filePathToPOSIX(Path.join(folderPath, Path.dirname(subdir))))
possiblePaths.push(fileUtils.filePathToPOSIX(Path.join(folderPath, Path.dirname(subdir)))) }
}
const libraryItem = await Database.libraryItemModel.findOne({ const libraryItem = await Database.libraryItemModel.findOne({
where: { where: {
path: possiblePaths path: possiblePaths
} }
})
if (libraryItem) {
return res.json({
exists: true,
libraryItemTitle: libraryItem.title
}) })
if (libraryItem) {
return res.json({
exists: true,
libraryItemTitle: libraryItem.title
})
}
} }
} }

View File

@ -246,9 +246,10 @@ class MediaProgress extends Model {
// For local sync // For local sync
if (progressPayload.lastUpdate) { if (progressPayload.lastUpdate) {
this.updatedAt = progressPayload.lastUpdate this.updatedAt = progressPayload.lastUpdate
this.changed('updatedAt', true)
} }
return this.save() return this.save({ silent: !!progressPayload.lastUpdate })
} }
} }

View File

@ -205,7 +205,7 @@ function extractEpisodeData(item) {
} else if (typeof guidItem?._ === 'string') { } else if (typeof guidItem?._ === 'string') {
episode.guid = guidItem._ episode.guid = guidItem._
} else { } else {
Logger.error(`[podcastUtils] Invalid guid ${item['guid']} for ${episode.enclosure.url}`) Logger.error(`[podcastUtils] Invalid guid for ${episode.enclosure.url}`, item['guid'])
} }
} }

View File

@ -149,11 +149,12 @@ module.exports = {
libraryId libraryId
} }
const libraryItemIncludes = [] const libraryItemIncludes = []
if (includeRSSFeed) { if (filterGroup === 'feed-open' || includeRSSFeed) {
const rssFeedRequired = filterGroup === 'feed-open'
libraryItemIncludes.push({ libraryItemIncludes.push({
model: Database.feedModel, model: Database.feedModel,
required: filterGroup === 'feed-open', required: rssFeedRequired,
separate: true separate: !rssFeedRequired
}) })
} }
if (filterGroup === 'issues') { if (filterGroup === 'issues') {