summaryrefslogtreecommitdiff
path: root/rapid/scan.py
diff options
context:
space:
mode:
Diffstat (limited to 'rapid/scan.py')
-rwxr-xr-xrapid/scan.py128
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