Fix:Podcast episodes duplicated when a scan runs while the episode is downloading #2785

This commit is contained in:
advplyr 2024-11-07 17:26:51 -06:00
parent a5ebd89817
commit 850ed48955
5 changed files with 40 additions and 6 deletions

View File

@ -149,6 +149,9 @@ class Server {
Watcher.disabled = true Watcher.disabled = true
} else { } else {
Watcher.initWatcher(libraries) Watcher.initWatcher(libraries)
Watcher.on('scanFilesChanged', (pendingFileUpdates, pendingTask) => {
LibraryScanner.scanFilesChanged(pendingFileUpdates, pendingTask)
})
} }
} }

View File

@ -2,7 +2,6 @@ const Path = require('path')
const EventEmitter = require('events') const EventEmitter = require('events')
const Watcher = require('./libs/watcher/watcher') const Watcher = require('./libs/watcher/watcher')
const Logger = require('./Logger') const Logger = require('./Logger')
const LibraryScanner = require('./scanner/LibraryScanner')
const Task = require('./objects/Task') const Task = require('./objects/Task')
const TaskManager = require('./managers/TaskManager') const TaskManager = require('./managers/TaskManager')
@ -31,6 +30,8 @@ class FolderWatcher extends EventEmitter {
this.filesBeingAdded = new Set() this.filesBeingAdded = new Set()
/** @type {Set<string>} */
this.ignoreFilePathsDownloading = new Set()
/** @type {string[]} */ /** @type {string[]} */
this.ignoreDirs = [] this.ignoreDirs = []
/** @type {string[]} */ /** @type {string[]} */
@ -333,7 +334,7 @@ class FolderWatcher extends EventEmitter {
} }
if (this.pendingFileUpdates.length) { if (this.pendingFileUpdates.length) {
LibraryScanner.scanFilesChanged(this.pendingFileUpdates, this.pendingTask) this.emit('scanFilesChanged', this.pendingFileUpdates, this.pendingTask)
} else { } else {
const taskFinishedString = { const taskFinishedString = {
text: 'No files to scan', text: 'No files to scan',
@ -348,12 +349,29 @@ class FolderWatcher extends EventEmitter {
}, this.pendingDelay) }, this.pendingDelay)
} }
/**
*
* @param {string} path
* @returns {boolean}
*/
checkShouldIgnorePath(path) { checkShouldIgnorePath(path) {
return !!this.ignoreDirs.find((dirpath) => { return !!this.ignoreDirs.find((dirpath) => {
return isSameOrSubPath(dirpath, path) return isSameOrSubPath(dirpath, path)
}) })
} }
/**
* When scanning a library item folder these files should be ignored
* Either a podcast episode downloading or a file that is pending by the watcher
*
* @param {string} path
* @returns {boolean}
*/
checkShouldIgnoreFilePath(path) {
if (this.pendingFilePaths.includes(path)) return true
return this.ignoreFilePathsDownloading.has(path)
}
/** /**
* Convert to POSIX and remove trailing slash * Convert to POSIX and remove trailing slash
* @param {string} path * @param {string} path

View File

@ -97,6 +97,7 @@ class PodcastManager {
// Ignores all added files to this dir // Ignores all added files to this dir
Watcher.addIgnoreDir(this.currentDownload.libraryItem.path) Watcher.addIgnoreDir(this.currentDownload.libraryItem.path)
Watcher.ignoreFilePathsDownloading.add(this.currentDownload.targetPath)
// Make sure podcast library item folder exists // Make sure podcast library item folder exists
if (!(await fs.pathExists(this.currentDownload.libraryItem.path))) { if (!(await fs.pathExists(this.currentDownload.libraryItem.path))) {
@ -151,6 +152,8 @@ class PodcastManager {
SocketAuthority.emitter('episode_download_queue_updated', this.getDownloadQueueDetails()) SocketAuthority.emitter('episode_download_queue_updated', this.getDownloadQueueDetails())
Watcher.removeIgnoreDir(this.currentDownload.libraryItem.path) Watcher.removeIgnoreDir(this.currentDownload.libraryItem.path)
Watcher.ignoreFilePathsDownloading.delete(this.currentDownload.targetPath)
this.currentDownload = null this.currentDownload = null
if (this.downloadQueue.length) { if (this.downloadQueue.length) {
this.startPodcastEpisodeDownload(this.downloadQueue.shift()) this.startPodcastEpisodeDownload(this.downloadQueue.shift())

View File

@ -1,6 +1,6 @@
const Path = require('path') const Path = require('path')
const uuidv4 = require("uuid").v4 const uuidv4 = require('uuid').v4
const { sanitizeFilename } = require('../utils/fileUtils') const { sanitizeFilename, filePathToPOSIX } = require('../utils/fileUtils')
const globals = require('../utils/globals') const globals = require('../utils/globals')
class PodcastEpisodeDownload { class PodcastEpisodeDownload {
@ -60,7 +60,7 @@ class PodcastEpisodeDownload {
return sanitizeFilename(filename) return sanitizeFilename(filename)
} }
get targetPath() { get targetPath() {
return Path.join(this.libraryItem.path, this.targetFilename) return filePathToPOSIX(Path.join(this.libraryItem.path, this.targetFilename))
} }
get targetRelPath() { get targetRelPath() {
return this.targetFilename return this.targetFilename
@ -74,7 +74,8 @@ class PodcastEpisodeDownload {
this.podcastEpisode = podcastEpisode this.podcastEpisode = podcastEpisode
const url = podcastEpisode.enclosure.url const url = podcastEpisode.enclosure.url
if (decodeURIComponent(url) !== url) { // Already encoded if (decodeURIComponent(url) !== url) {
// Already encoded
this.url = url this.url = url
} else { } else {
this.url = encodeURI(url) this.url = encodeURI(url)

View File

@ -4,7 +4,9 @@ const { LogLevel, ScanResult } = require('../utils/constants')
const fileUtils = require('../utils/fileUtils') const fileUtils = require('../utils/fileUtils')
const scanUtils = require('../utils/scandir') const scanUtils = require('../utils/scandir')
const libraryFilters = require('../utils/queries/libraryFilters') const libraryFilters = require('../utils/queries/libraryFilters')
const Logger = require('../Logger')
const Database = require('../Database') const Database = require('../Database')
const Watcher = require('../Watcher')
const LibraryScan = require('./LibraryScan') const LibraryScan = require('./LibraryScan')
const LibraryItemScanData = require('./LibraryItemScanData') const LibraryItemScanData = require('./LibraryItemScanData')
const BookScanner = require('./BookScanner') const BookScanner = require('./BookScanner')
@ -128,6 +130,13 @@ class LibraryItemScanner {
const libraryFiles = [] const libraryFiles = []
for (let i = 0; i < fileItems.length; i++) { for (let i = 0; i < fileItems.length; i++) {
const fileItem = fileItems[i] const fileItem = fileItems[i]
if (Watcher.checkShouldIgnoreFilePath(fileItem.fullpath)) {
// Skip file if it's pending
Logger.info(`[LibraryItemScanner] Skipping watcher pending file "${fileItem.fullpath}" during scan of library item path "${libraryItemPath}"`)
continue
}
const newLibraryFile = new LibraryFile() const newLibraryFile = new LibraryFile()
// fileItem.path is the relative path // fileItem.path is the relative path
await newLibraryFile.setDataFromPath(fileItem.fullpath, fileItem.path) await newLibraryFile.setDataFromPath(fileItem.fullpath, fileItem.path)