diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py index ef313702db25493dacca2fea805e621f32a32c88..81c5473ff15e409b157bd08a0dda82a0bdbf69da 100644 --- a/sickbeard/helpers.py +++ b/sickbeard/helpers.py @@ -1519,7 +1519,7 @@ def verify_freespace(src, dest, oldfile=None): if not ek.ek(os.path.isfile, src): logger.log("A path to a file is required for the source. " + src + " is not a file.", logger.WARNING) - return False + return True try: diskfree = disk_usage(dest) @@ -1555,3 +1555,36 @@ def pretty_time_delta(seconds): return '%s%02dm%02ds' % (sign_string, minutes, seconds) else: return '%s%02ds' % (sign_string, seconds) + +def isFileLocked(file, writeLockCheck=False): + ''' + Checks to see if a file is locked. Performs three checks + 1. Checks if the file even exists + 2. Attempts to open the file for reading. This will determine if the file has a write lock. + Write locks occur when the file is being edited or copied to, e.g. a file copy destination + 3. If the readLockCheck parameter is True, attempts to rename the file. If this fails the + file is open by some other process for reading. The file can be read, but not written to + or deleted. + @param file: the file being checked + @param writeLockCheck: when true will check if the file is locked for writing (prevents move operations) + ''' + if(not(os.path.exists(file))): + return True + try: + f = open(file, 'r') + f.close() + except IOError: + return True + + if(writeLockCheck): + lockFile = file + ".lckchk" + if(os.path.exists(lockFile)): + os.remove(lockFile) + try: + os.rename(file, lockFile) + time.sleep(1) + os.rename(lockFile, file) + except WindowsError: + return True + + return False diff --git a/sickbeard/postProcessor.py b/sickbeard/postProcessor.py index 83c6119b0dad905eb0cfdda171cc3bd1101e88a5..fc5df375813ebd4f1e9efcb50c5043d3d61c1ef0 100644 --- a/sickbeard/postProcessor.py +++ b/sickbeard/postProcessor.py @@ -910,9 +910,13 @@ class PostProcessor(object): logger.DEBUG) # try to find out if we have enough space to perform the copy or move action. - if not verify_freespace(self.file_path, ek.ek(os.path.dirname, ep_obj.show._location), [ep_obj] + ep_obj.relatedEps): - self._log("Not enough space to continue PP, exiting") - return False + if not helpers.isFileLocked(self.file_path, False): + if not verify_freespace(self.file_path, ek.ek(os.path.dirname, ep_obj.show._location), [ep_obj] + ep_obj.relatedEps): + self._log("Not enough space to continue PP, exiting") + return False + else: + self._log("Unable to determine needed filespace as the source file is locked for access") + # delete the existing file (and company) for cur_ep in [ep_obj] + ep_obj.relatedEps: @@ -1019,15 +1023,21 @@ class PostProcessor(object): try: # move the episode and associated files to the show dir if self.process_method == "copy": + if helpers.isFileLocked(self.file_path, False): + raise exceptions.PostProcessingFailed("File is locked for reading") self._copy(self.file_path, dest_path, new_base_name, sickbeard.MOVE_ASSOCIATED_FILES, sickbeard.USE_SUBTITLES and ep_obj.show.subtitles) elif self.process_method == "move": + if helpers.isFileLocked(self.file_path, True): + raise exceptions.PostProcessingFailed("File is locked for reading/writing") self._move(self.file_path, dest_path, new_base_name, sickbeard.MOVE_ASSOCIATED_FILES, sickbeard.USE_SUBTITLES and ep_obj.show.subtitles) elif self.process_method == "hardlink": self._hardlink(self.file_path, dest_path, new_base_name, sickbeard.MOVE_ASSOCIATED_FILES, sickbeard.USE_SUBTITLES and ep_obj.show.subtitles) elif self.process_method == "symlink": + if helpers.isFileLocked(self.file_path, True): + raise exceptions.PostProcessingFailed("File is locked for reading/writing") self._moveAndSymlink(self.file_path, dest_path, new_base_name, sickbeard.MOVE_ASSOCIATED_FILES, sickbeard.USE_SUBTITLES and ep_obj.show.subtitles) else: