summaryrefslogtreecommitdiff
path: root/rapid/rapid.py
diff options
context:
space:
mode:
Diffstat (limited to 'rapid/rapid.py')
-rwxr-xr-xrapid/rapid.py198
1 files changed, 144 insertions, 54 deletions
diff --git a/rapid/rapid.py b/rapid/rapid.py
index efd8145..3603025 100755
--- a/rapid/rapid.py
+++ b/rapid/rapid.py
@@ -57,6 +57,8 @@ import prefsrapid
import tableplusminus as tpm
import generatename as gn
+import downloadtracker
+
from metadatavideo import DOWNLOAD_VIDEO
import metadataphoto
import metadatavideo
@@ -65,6 +67,8 @@ import scan as scan_process
import copyfiles
import subfolderfile
+import errorlog
+
import device as dv
import utilities
@@ -105,13 +109,6 @@ def date_time_human_readable(date, with_line_break=True):
else:
return _("%(date)s %(time)s") % {'date':date.strftime("%x"), 'time':date.strftime("%X")}
-def time_subseconds_human_readable(date, subseconds):
- return _("%(hour)s:%(minute)s:%(second)s:%(subsecond)s") % \
- {'hour':date.strftime("%H"),
- 'minute':date.strftime("%M"),
- 'second':date.strftime("%S"),
- 'subsecond': subseconds}
-
def date_time_subseconds_human_readable(date, subseconds):
return _("%(date)s %(hour)s:%(minute)s:%(second)s:%(subsecond)s") % \
{'date':date.strftime("%x"),
@@ -228,9 +225,9 @@ class DeviceCollection(gtk.TreeView):
else:
return None
- def update_progress(self, process_id, percent_complete, progress_bar_text, bytes_downloaded):
+ def update_progress(self, scan_pid, percent_complete, progress_bar_text, bytes_downloaded):
- iter = self._get_process_map(process_id)
+ iter = self._get_process_map(scan_pid)
if iter:
if percent_complete:
self.liststore.set_value(iter, 3, percent_complete)
@@ -558,6 +555,16 @@ class ThumbnailDisplay(gtk.IconView):
self.total_files += 1
+ def get_sample_file(self, file_type):
+ """Returns an rpd_file for of a given file type, or None if it does
+ not exist"""
+ for unique_id, rpd_file in self.rpd_files.iteritems():
+ if rpd_file.file_type == file_type:
+ if rpd_file.status <> STATUS_CANNOT_DOWNLOAD:
+ return rpd_file
+
+ return None
+
def get_unique_id_from_iter(self, iter):
return self.liststore.get_value(iter, 2)
@@ -862,6 +869,7 @@ class TaskManager:
def add_task(self, task):
pid = self._setup_task(task)
+ logger.debug("TaskManager PID: %s", pid)
return pid
@@ -1032,6 +1040,7 @@ class SubfolderFileManager(SingleInstanceTaskManager):
SingleInstanceTaskManager.__init__(self, results_callback)
self._subfolder_file = subfolderfile.SubfolderFile(self.task_process_conn, sequence_values)
self._subfolder_file.start()
+ logger.debug("SubfolderFile PID: %s", self._subfolder_file.pid)
def rename_file_and_move_to_subfolder(self, download_succeeded,
download_count, rpd_file):
@@ -1235,7 +1244,8 @@ class RapidApp(dbus.service.Object):
# FIXME: need more fine grained tuning here - must cancel large file
# copies midstream
- logger.info("Terminating...")
+ if terminate_file_copies:
+ logger.info("Terminating all processes...")
scan_termination_requested = self.scan_manager.request_termination()
thumbnails_termination_requested = self.thumbnails.thumbnail_manager.request_termination()
@@ -1760,11 +1770,7 @@ class RapidApp(dbus.service.Object):
# Track download sizes and other values for each device.
# (Scan id acts as an index to each device. A device could be scanned
# more than once).
- self.size_of_download_in_bytes_by_scan_pid = dict()
- self.no_files_in_download_by_scan_pid = dict()
- self.file_types_present_by_scan_pid = dict()
- self.download_count_for_file_by_unique_id = dict()
- self.download_count_by_scan_pid = dict()
+ self.download_tracker = downloadtracker.DownloadTracker()
# Track which temporary directories are created when downloading files
self.temp_dirs_by_scan_pid = dict()
@@ -1781,17 +1787,23 @@ class RapidApp(dbus.service.Object):
self.download_start_time = datetime.datetime.now()
files_by_scan_pid = self.thumbnails.get_files_checked_for_download()
- folders_valid = self.check_download_folder_validity(files_by_scan_pid)
-
- #FIXME: if invalid, display some kind of error message to the user
+ folders_valid, invalid_dirs = self.check_download_folder_validity(files_by_scan_pid)
- if folders_valid:
+ if not folders_valid:
+ if len(invalid_dirs) > 1:
+ msg = _("These download folders are invalid:\n%(folder1)s\n%(folder2)s") % {
+ 'folder1': invalid_dirs[0], 'folder2': invalid_dirs[1]}
+ else:
+ msg = _("This download folder is invalid:\n%s") % invalid_dirs[0]
+ self.log_error(config.CRITICAL_ERROR, _("Download cannot proceed"),
+ msg)
+ else:
self.thumbnails.mark_download_pending(files_by_scan_pid)
for scan_pid in files_by_scan_pid:
files = files_by_scan_pid[scan_pid]
self.download_files(files, scan_pid)
- self.set_download_action_label(is_download = False)
+ self.set_download_action_label(is_download = False)
def pause_download(self):
@@ -1820,8 +1832,10 @@ class RapidApp(dbus.service.Object):
else:
video_download_folder = None
- self.size_of_download_in_bytes_by_scan_pid[scan_pid] = self.size_files_to_be_downloaded(files)
- self.no_files_in_download_by_scan_pid[scan_pid] = len(files)
+ self.download_tracker.init_stats(scan_pid=scan_pid,
+ bytes=self.size_files_to_be_downloaded(files),
+ no_files=len(files))
+
self.download_active_by_scan_pid.append(scan_pid)
# Initiate copy files process
self.copy_files_manager.add_task((photo_download_folder,
@@ -1843,16 +1857,18 @@ class RapidApp(dbus.service.Object):
self.temp_dirs_by_scan_pid[scan_pid] = (photo_temp_dir, video_temp_dir)
elif msg_type == rpdmp.MSG_BYTES:
scan_pid, total_downloaded = data
- percent_complete = (float(total_downloaded) /
- self.size_of_download_in_bytes_by_scan_pid[scan_pid]) * 100
+ self.download_tracker.set_total_bytes_copied(scan_pid,
+ total_downloaded)
+ percent_complete = self.download_tracker.get_percent_complete(scan_pid)
self.device_collection.update_progress(scan_pid, percent_complete,
None, None)
elif msg_type == rpdmp.MSG_FILE:
download_succeeded, rpd_file, download_count, temp_full_file_name = data
-
- self.download_count_for_file_by_unique_id[rpd_file.unique_id] = download_count
- self.download_count_by_scan_pid[rpd_file.scan_pid] = download_count
+ self.download_tracker.set_download_count_for_file(
+ rpd_file.unique_id, download_count)
+ self.download_tracker.set_download_count(
+ rpd_file.scan_pid, download_count)
rpd_file.download_start_time = self.download_start_time
if download_succeeded:
@@ -1860,9 +1876,8 @@ class RapidApp(dbus.service.Object):
rpd_file = prefsrapid.insert_pref_lists(self.prefs, rpd_file)
rpd_file.strip_characters = self.prefs.strip_characters
rpd_file.download_folder = self.prefs.get_download_folder_for_file_type(rpd_file.file_type)
-
- #~ if not download_succeeded:
- #~ logger.error("File was not downloaded: %s", rpd_file.full_file_name)
+ rpd_file.download_conflict_resolution = self.prefs.download_conflict_resolution
+ rpd_file.synchronize_raw_jpg = self.prefs.must_synchronize_raw_jpg()
self.subfolder_file_manager.rename_file_and_move_to_subfolder(
download_succeeded,
@@ -1896,17 +1911,25 @@ class RapidApp(dbus.service.Object):
scan_pid = rpd_file.scan_pid
unique_id = rpd_file.unique_id
- self._update_file_download_device_progress(scan_pid, unique_id)
-
self.thumbnails.update_status_post_download(rpd_file)
- download_count = self.download_count_for_file_by_unique_id[unique_id]
- if download_count == self.no_files_in_download_by_scan_pid[scan_pid]:
+ # Update error log window if neccessary
+ if not move_succeeded:
+ self.log_error(config.SERIOUS_ERROR, rpd_file.error_title,
+ rpd_file.error_msg, rpd_file.error_extra_detail)
+ elif rpd_file.status == config.STATUS_DOWNLOADED_WITH_WARNING:
+ self.log_error(config.WARNING, rpd_file.error_title,
+ rpd_file.error_msg, rpd_file.error_extra_detail)
+
+ self.download_tracker.file_downloaded_increment(scan_pid)
+ self._update_file_download_device_progress(scan_pid, unique_id)
+
+ download_count = self.download_tracker.get_download_count_for_file(unique_id)
+ if download_count == self.download_tracker.get_no_files_in_download(scan_pid):
# Last file has been downloaded, so clean temp directory
logger.debug("Purging temp directories")
self._clean_temp_dirs_for_scan_pid(scan_pid)
- del self.no_files_in_download_by_scan_pid[scan_pid]
- del self.size_of_download_in_bytes_by_scan_pid[scan_pid]
+ self.download_tracker.purge(scan_pid)
self.download_active_by_scan_pid.remove(scan_pid)
if not self.download_is_occurring():
@@ -1931,13 +1954,15 @@ class RapidApp(dbus.service.Object):
"""
Increments the progress bar for an individual device
"""
- #~ scan_pid = rpd_file.scan_pid
- #~ unique_id = rpd_file.unique_id
progress_bar_text = _("%(number)s of %(total)s %(filetypes)s") % \
- {'number': self.download_count_for_file_by_unique_id[unique_id],
- 'total': self.no_files_in_download_by_scan_pid[scan_pid],
- 'filetypes': self.file_types_present_by_scan_pid[scan_pid]}
- self.device_collection.update_progress(scan_pid, None, progress_bar_text, None)
+ {'number': self.download_tracker.get_download_count_for_file(unique_id),
+ 'total': self.download_tracker.get_no_files_in_download(scan_pid),
+ 'filetypes': self.download_tracker.get_file_types_present(scan_pid)}
+ percent_complete = self.download_tracker.get_percent_complete(scan_pid)
+ self.device_collection.update_progress(scan_pid=scan_pid,
+ percent_complete=percent_complete,
+ progress_bar_text=progress_bar_text,
+ bytes_downloaded=None)
def _clean_all_temp_dirs(self):
"""
@@ -1998,7 +2023,7 @@ class RapidApp(dbus.service.Object):
self.prefs.video_rename,
self.prefs.video_subfolder)
if not prefs_ok:
- logger.error("There is an error in the program preferences. Some preferences will be reset.")
+ logger.error("There is an error in the program preferences relating to file renaming and subfolder creation. Some preferences will be reset.")
return prefs_ok
@@ -2096,6 +2121,8 @@ class RapidApp(dbus.service.Object):
self.thumbnails.clear_all()
self.setup_devices(on_startup = False, on_preference_change = True, do_not_allow_auto_start = True)
+ if self.main_notebook.get_current_page() == 1: # preview of file
+ self.main_notebook.set_current_page(0)
self.rerun_setup_available_image_and_video_media = False
@@ -2139,6 +2166,7 @@ class RapidApp(dbus.service.Object):
self.device_collection_scrolledwindow = builder.get_object("device_collection_scrolledwindow")
self.next_image_action = builder.get_object("next_image_action")
self.prev_image_action = builder.get_object("prev_image_action")
+ self.menu_log_window = builder.get_object("menu_log_window")
# Only enable this action when actually displaying a preview
self.next_image_action.set_sensitive(False)
@@ -2161,6 +2189,9 @@ class RapidApp(dbus.service.Object):
self.device_collection = DeviceCollection(self)
self.device_collection_viewport.add(self.device_collection)
+ #error log window
+ self.error_log = errorlog.ErrorLog(self)
+
# monitor to handle mounts and dismounts
self.vmonitor = None
# track scan ids for mount paths - very useful when a device is unmounted
@@ -2258,8 +2289,8 @@ class RapidApp(dbus.service.Object):
Also displays backup volumes / path being used. (NOT IMPLEMENTED YET)
"""
- photo_dir = self.is_valid_download_dir(self.prefs.download_folder)
- video_dir = self.is_valid_download_dir(self.prefs.video_download_folder)
+ photo_dir = self.is_valid_download_dir(path=self.prefs.download_folder, is_photo_dir=True, show_error_in_log=True)
+ video_dir = self.is_valid_download_dir(path=self.prefs.video_download_folder, is_photo_dir=False, show_error_in_log=True)
if photo_dir and video_dir:
same_file_system = self.same_file_system(self.prefs.download_folder,
self.prefs.video_download_folder)
@@ -2272,6 +2303,7 @@ class RapidApp(dbus.service.Object):
if video_dir and not same_file_system:
dirs.append((self.prefs.video_download_folder, _("videos")))
+ msg = ''
if len(dirs) > 1:
msg = ' ' + _('Free space:') + ' '
@@ -2313,7 +2345,7 @@ class RapidApp(dbus.service.Object):
# user manually specified backup location
msg2 = _('Backing up to %(path)s') % {'path':self.prefs.backup_location}
else:
- msg2 = self.displayBackupVolumes()
+ msg2 = self.displayBackupVolumes() #FIXME
if msg:
msg = _("%(freespace)s. %(backuppaths)s.") % {'freespace': msg, 'backuppaths': msg2}
@@ -2323,6 +2355,27 @@ class RapidApp(dbus.service.Object):
msg = msg.rstrip()
self.statusbar_message(msg)
+
+ def log_error(self, severity, problem, details, extra_detail=None):
+ """
+ Display error and warning messages to user in log window
+ """
+ self.error_log.add_message(severity, problem, details, extra_detail)
+
+
+ def on_error_eventbox_button_press_event(self, widget, event):
+ self.prefs.show_log_dialog = True
+ self.error_log.widget.show()
+
+
+ def on_menu_log_window_toggled(self, widget):
+ active = widget.get_active()
+ self.prefs.show_log_dialog = active
+ if active:
+ self.error_log.widget.show()
+ else:
+ self.error_log.widget.hide()
+
# # #
# Utility functions
@@ -2352,8 +2405,12 @@ class RapidApp(dbus.service.Object):
"""
Checks validity of download folders based on the file types the user
is attempting to download.
+
+ If valid, returns a tuple of True and an empty list.
+ If invalid, returns a tuple of False and a list of the invalid directores.
"""
valid = True
+ invalid_dirs = []
# first, check what needs to be downloaded - photos and / or videos
need_photo_folder = False
need_video_folder = False
@@ -2369,14 +2426,18 @@ class RapidApp(dbus.service.Object):
# second, check validity
if need_photo_folder:
- if not self.is_valid_download_dir(self.prefs.download_folder):
+ if not self.is_valid_download_dir(self.prefs.download_folder,
+ is_photo_dir=True):
valid = False
+ invalid_dirs.append(self.prefs.download_folder)
if need_video_folder:
- if not self.is_valid_download_dir(self.prefs.video_download_folder):
+ if not self.is_valid_download_dir(self.prefs.video_download_folder,
+ is_photo_dir=False):
valid = False
+ invalid_dirs.append(self.prefs.video_download_folder)
- return valid
+ return (valid, invalid_dirs)
def same_file_system(self, file1, file2):
"""Returns True if the files / diretories are on the same file system
@@ -2403,24 +2464,47 @@ class RapidApp(dbus.service.Object):
f2_id = f2_info.get_attribute_string(gio.FILE_ATTRIBUTE_ID_FILE)
return f1_id == f2_id
- def is_valid_download_dir(self, path):
+ def is_valid_download_dir(self, path, is_photo_dir, show_error_in_log=False):
"""
Checks the following conditions:
Does the directory exist?
Is it writable?
+
+ if show_error_in_log is True, then display warning in log window, using
+ is_photo_dir, which if true means the download directory is for photos,
+ if false, for Videos
"""
valid = False
+ if is_photo_dir:
+ download_folder_type = _("Photo")
+ else:
+ download_folder_type = _("Video")
+
try:
d = gio.File(path)
if not d.query_exists(cancellable=None):
- logger.error("Download directory does not exist: %s", path)
+ logger.error("%s download folder does not exist: %s",
+ download_folder_type, path)
+ if show_error_in_log:
+ severity = config.WARNING
+ problem = _("%(file_type)s download folder does not exist") % {
+ 'file_type': download_folder_type}
+ details = _("Folder: %s") % path
+ self.log_error(severity, problem, details)
else:
file_attributes = "standard::type,access::can-read,access::can-write"
file_info = d.query_filesystem_info(file_attributes)
file_type = file_info.get_file_type()
if file_type != gio.FILE_TYPE_DIRECTORY and file_type != gio.FILE_TYPE_UNKNOWN:
- logger.error("%s is an invalid directory", path)
+ logger.error("%s download folder is invalid: %s",
+ download_folder_type, path)
+ if show_error_in_log:
+ severity = config.WARNING
+ problem = _("%(file_type)s download folder is invalid") % {
+ 'file_type': download_folder_type}
+ details = _("Folder: %s") % path
+ self.log_error(severity, problem, details)
else:
# is the directory writable?
try:
@@ -2428,6 +2512,12 @@ class RapidApp(dbus.service.Object):
valid = True
except:
logger.error("%s is not writable", path)
+ if show_error_in_log:
+ severity = config.WARNING
+ problem = _("%(file_type)s download folder is not writable") % {
+ 'file_type': download_folder_type}
+ details = _("Folder: %s") % path
+ self.log_error(severity, problem, details)
else:
f = gio.File(temp_dir)
f.delete(cancellable=None)
@@ -2489,7 +2579,7 @@ class RapidApp(dbus.service.Object):
size, file_type_counter, scan_pid = data
size = format_size_for_user(bytes=size)
results_summary, file_types_present = file_type_counter.summarize_file_count()
- self.file_types_present_by_scan_pid[scan_pid] = file_types_present
+ self.download_tracker.set_file_types_present(scan_pid, file_types_present)
logger.info('Found %s' % results_summary)
logger.info('Files total %s' % size)
self.device_collection.update_device(scan_pid, size)
@@ -2508,7 +2598,7 @@ class RapidApp(dbus.service.Object):
return False
else:
if len(data) > self.batch_size:
- logger.error("incoming pipe length is %s" % len(data))
+ logger.critical("incoming pipe length is unexpectedly long: %s" % len(data))
else:
for rpd_file in data:
self.thumbnails.add_file(rpd_file)