diff options
Diffstat (limited to 'rapid/scan.py')
-rwxr-xr-x | rapid/scan.py | 128 |
1 files changed, 72 insertions, 56 deletions
diff --git a/rapid/scan.py b/rapid/scan.py index 637031a..f0e4422 100755 --- a/rapid/scan.py +++ b/rapid/scan.py @@ -42,59 +42,71 @@ file_attributes = "standard::name,standard::display-name,\ standard::type,standard::size,time::modified,access::can-read,id::file" -def get_video_THM_file(full_file_name_no_ext): - """ - Checks to see if a thumbnail file (THM) is in the same directory as the - file. Expects a full path to be part of the file name. - - Returns the filename, including path, if found, else returns None. - """ - +def get_associated_file(full_file_name_no_ext, extensions_to_check): + f = None - for e in rpdfile.VIDEO_THUMBNAIL_EXTENSIONS: + for e in extensions_to_check: if os.path.exists(full_file_name_no_ext + '.' + e): f = full_file_name_no_ext + '.' + e break if os.path.exists(full_file_name_no_ext + '.' + e.upper()): f = full_file_name_no_ext + '.' + e.upper() break - - return f + return f + +def get_audio_file(full_file_name_no_ext): + """ + Checks to see if an audio file of the same name is in the same directory + as the file. Expects a full path to be part of the file name. + + Returns the filename, including path, if found, else returns None. + """ + return get_associated_file(full_file_name_no_ext, rpdfile.AUDIO_EXTENSIONS) + +def get_video_THM_file(full_file_name_no_ext): + """ + Checks to see if a thumbnail file (THM) is in the same directory as the + file. Expects a full path to be part of the file name. + + Returns the filename, including path, if found, else returns None. + """ + + return get_associated_file(full_file_name_no_ext, rpdfile.VIDEO_THUMBNAIL_EXTENSIONS) class Scan(multiprocessing.Process): """Scans the given path for files of a specified type. - + Returns results in batches, finishing with a total of the size of all the files in bytes. """ - + def __init__(self, path, ignored_paths, use_re_ignored_paths, - batch_size, results_pipe, + batch_size, results_pipe, terminate_queue, run_event): - + """Setup values needed to conduct the scan. - + 'path' is a string of the path to be scanned, which is passed to gio. - + 'ignored_paths' is a list of paths that should not be scanned. Any path ending with one of the values will be ignored. - + 'use_re_ignored_paths': if true, pytho regular expressions will be used to determine which paths to ignore - - 'batch_size' is the number of files that should be sent back to the + + 'batch_size' is the number of files that should be sent back to the calling function at one time. - + 'results_pipe' is a connection on which to send the results. - - 'terminate_queue' is a queue whose sole purpose is to notify the + + 'terminate_queue' is a queue whose sole purpose is to notify the process that it should terminate and not return any results. - + 'run_event' is an Event that is used to temporarily halt execution. - + """ - + multiprocessing.Process.__init__(self) self.path = path self.ignored_paths = ignored_paths @@ -107,18 +119,18 @@ class Scan(multiprocessing.Process): self.files_scanned = 0 self.files = [] self.file_type_counter = rpdfile.FileTypeCounter() - + def _gio_scan(self, path, file_size_sum): """recursive function to scan a directory and its subdirectories for photos and possibly videos""" - + children = path.enumerate_children(file_attributes) - + for child in children: - + # pause if instructed by the caller self.run_event.wait() - + if not self.terminate_queue.empty(): x = self.terminate_queue.get() # terminate immediately @@ -128,26 +140,26 @@ class Scan(multiprocessing.Process): # only collect files and scan in directories we can actually read # cannot assume that users will download only from memory cards - + if child.get_attribute_boolean(gio.FILE_ATTRIBUTE_ACCESS_CAN_READ): file_type = child.get_file_type() name = child.get_name() if file_type == gio.FILE_TYPE_DIRECTORY: if not self.ignore_this_path(name): - file_size_sum = self._gio_scan(path.get_child(name), + file_size_sum = self._gio_scan(path.get_child(name), file_size_sum) if file_size_sum is None: return None elif file_type == gio.FILE_TYPE_REGULAR: - + self.files_scanned += 1 if self.files_scanned % 100 == 0: logger.debug("Scanned %s files", self.files_scanned) - + base_name, ext = os.path.splitext(name) ext = ext.lower()[1:] - + file_type = rpdfile.file_type(ext) if file_type is not None: file_id = child.get_attribute_string( @@ -161,31 +173,35 @@ class Scan(multiprocessing.Process): size = child.get_size() modification_time = child.get_modification_time() path_name = path.get_path() - + # look for thumbnail file for videos if file_type == rpdfile.FILE_TYPE_VIDEO: thm_full_name = get_video_THM_file(os.path.join(path_name, base_name)) else: thm_full_name = None - - scanned_file = rpdfile.get_rpdfile(ext, - name, - display_name, + + # check if an audio file is associated with the photo or video + audio_file_full_name = get_audio_file(os.path.join(path_name, base_name)) + + scanned_file = rpdfile.get_rpdfile(ext, + name, + display_name, path_name, size, modification_time, - thm_full_name, + thm_full_name, + audio_file_full_name, self.pid, file_id, file_type) - + self.files.append(scanned_file) file_size_sum += size - + if self.counter == self.batch_size: # send batch of results - self.results_pipe.send((rpdmp.CONN_PARTIAL, - (file_size_sum, + self.results_pipe.send((rpdmp.CONN_PARTIAL, + (file_size_sum, self.file_type_counter, self.pid, self.files))) @@ -193,14 +209,14 @@ class Scan(multiprocessing.Process): self.counter = 0 return file_size_sum - + def run(self): """start the actual scan.""" - + if self.use_re_ignored_paths and len(self.ignored_paths): self.re_pattern = prefsrapid.check_and_compile_re(self.ignored_paths) - + source = gio.File(self.path) try: if not self.ignore_this_path(self.path): @@ -210,25 +226,25 @@ class Scan(multiprocessing.Process): except gio.Error, inst: logger.error("Error while scanning %s: %s", self.path, inst) size = None - + if size is not None: if self.counter > 0: # send any remaining results - self.results_pipe.send((rpdmp.CONN_PARTIAL, (size, + self.results_pipe.send((rpdmp.CONN_PARTIAL, (size, self.file_type_counter, self.pid, self.files))) - - self.results_pipe.send((rpdmp.CONN_COMPLETE, (size, + + self.results_pipe.send((rpdmp.CONN_COMPLETE, (size, self.file_type_counter, self.pid))) - self.results_pipe.close() + self.results_pipe.close() def ignore_this_path(self, path): """ determines if the path should be ignored according to the preferences chosen by the user """ - + if len(self.ignored_paths): if self.use_re_ignored_paths and self.re_pattern: # regular expressions are being used @@ -238,5 +254,5 @@ class Scan(multiprocessing.Process): # regular expressions are not being used if path.endswith(tuple(self.ignored_paths)): return True - + return False |