summaryrefslogtreecommitdiff
path: root/rapid/rapid.py
diff options
context:
space:
mode:
Diffstat (limited to 'rapid/rapid.py')
-rwxr-xr-xrapid/rapid.py228
1 files changed, 150 insertions, 78 deletions
diff --git a/rapid/rapid.py b/rapid/rapid.py
index 4f7e6bc..dd67b63 100755
--- a/rapid/rapid.py
+++ b/rapid/rapid.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# -*- coding: latin1 -*-
-### Copyright (C) 2011-2012 Damon Lynch <damonlynch@gmail.com>
+### Copyright (C) 2011-2014 Damon Lynch <damonlynch@gmail.com>
### This program is free software; you can redistribute it and/or modify
### it under the terms of the GNU General Public License as published by
@@ -743,39 +743,40 @@ class ThumbnailDisplay(gtk.IconView):
self.previews_being_fetched.add(unique_id)
def show_preview(self, unique_id=None, iter=None):
- if unique_id is not None:
- iter = self.get_iter_from_unique_id(unique_id)
- elif iter is not None:
- unique_id = self.get_unique_id_from_iter(iter)
- else:
- # neither an iter or a unique_id were passed
- # use iter from first selected file
- # if none is selected, choose the first file
- selected = self.get_selected_items()
- if selected:
- path = selected[0]
+ if len(self.liststore):
+ if unique_id is not None:
+ iter = self.get_iter_from_unique_id(unique_id)
+ elif iter is not None:
+ unique_id = self.get_unique_id_from_iter(iter)
else:
- path = 0
- iter = self.liststore.get_iter(path)
- unique_id = self.get_unique_id_from_iter(iter)
+ # neither an iter or a unique_id were passed
+ # use iter from first selected file
+ # if none is selected, choose the first file
+ selected = self.get_selected_items()
+ if selected:
+ path = selected[0]
+ else:
+ path = 0
+ iter = self.liststore.get_iter(path)
+ unique_id = self.get_unique_id_from_iter(iter)
- rpd_file = self.rpd_files[unique_id]
+ rpd_file = self.rpd_files[unique_id]
- if unique_id in self.previews:
- preview_image = self.previews[unique_id]
- else:
- # request daemon process to get a full size thumbnail
- self._get_preview(unique_id, rpd_file)
- if unique_id in self.thumbnails:
- preview_image = self.thumbnails[unique_id]
+ if unique_id in self.previews:
+ preview_image = self.previews[unique_id]
else:
- preview_image = self.get_stock_icon(rpd_file.file_type)
+ # request daemon process to get a full size thumbnail
+ self._get_preview(unique_id, rpd_file)
+ if unique_id in self.thumbnails:
+ preview_image = self.thumbnails[unique_id]
+ else:
+ preview_image = self.get_stock_icon(rpd_file.file_type)
- checked = self.liststore.get_value(iter, self.SELECTED_COL)
- include_checkbutton_visible = rpd_file.status == STATUS_NOT_DOWNLOADED
- self.rapid_app.show_preview_image(unique_id, preview_image,
- include_checkbutton_visible, checked)
+ checked = self.liststore.get_value(iter, self.SELECTED_COL)
+ include_checkbutton_visible = rpd_file.status == STATUS_NOT_DOWNLOADED
+ self.rapid_app.show_preview_image(unique_id, preview_image,
+ include_checkbutton_visible, checked)
def _get_next_iter(self, iter):
iter = self.liststore.iter_next(iter)
@@ -1249,12 +1250,14 @@ class CopyFilesManager(TaskManager):
video_download_folder = task[1]
scan_pid = task[2]
files = task[3]
- modify_files_during_download = task[4]
- modify_pipe = task[5]
+ verify_files = task[4]
+ modify_files_during_download = task[5]
+ modify_pipe = task[6]
copy_files = copyfiles.CopyFiles(photo_download_folder,
video_download_folder,
files,
+ verify_files,
modify_files_during_download,
modify_pipe,
scan_pid, self.batch_size,
@@ -1276,7 +1279,7 @@ class ThumbnailManager(TaskManager):
return generator.pid
class FileModifyManager(TaskManager):
- """Handles the modification of downloaded files before they are renamed
+ """Handles the modification or verification of of downloaded files before they are renamed
Duplex, multiprocess, similar to BackupFilesManager
"""
def __init__(self, results_callback):
@@ -1289,8 +1292,11 @@ class FileModifyManager(TaskManager):
scan_pid = task[0]
auto_rotate_jpeg = task[1]
focal_length = task[2]
+ verify_file = task[3]
+ refresh_md5_on_file_change = task[4]
file_modify = filemodify.FileModify(auto_rotate_jpeg, focal_length,
+ verify_file, refresh_md5_on_file_change,
task_process_conn, terminate_queue,
run_event)
file_modify.start()
@@ -1329,7 +1335,7 @@ class BackupFilesManager(TaskManager):
def _send_termination_msg(self, p):
p[1].put(None)
- p[3].send((None, None, None, None, None))
+ p[3].send((None, None, None, None, None, None, None))
def _initiate_task(self, task, task_results_conn, task_process_conn,
terminate_queue, run_event):
@@ -1350,6 +1356,7 @@ class BackupFilesManager(TaskManager):
def backup_file(self, move_succeeded, rpd_file, path_suffix,
backup_duplicate_overwrite,
+ verify_file,
download_count):
if rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO:
@@ -1359,15 +1366,21 @@ class BackupFilesManager(TaskManager):
for path in self.backup_devices_by_path:
backup_type = self.backup_devices_by_path[path][2]
- if ((backup_type == PHOTO_VIDEO_BACKUP) or
+ do_backup = ((backup_type == PHOTO_VIDEO_BACKUP) or
(rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO and backup_type == PHOTO_BACKUP) or
- (rpd_file.file_type == rpdfile.FILE_TYPE_VIDEO and backup_type == VIDEO_BACKUP)):
+ (rpd_file.file_type == rpdfile.FILE_TYPE_VIDEO and backup_type == VIDEO_BACKUP))
+ if do_backup:
logger.debug("Backing up to %s", path)
- task_results_conn = self.backup_devices_by_path[path][0]
- task_results_conn.send((move_succeeded, rpd_file, path_suffix,
- backup_duplicate_overwrite, download_count))
else:
logger.debug("Not backing up to %s", path)
+ # Even if not going to backup to this device, need to send it anyway so
+ # progress bar can be updated. Not this most efficient but the
+ # code is much more simple
+ task_results_conn = self.backup_devices_by_path[path][0]
+ task_results_conn.send((move_succeeded, do_backup, rpd_file,
+ path_suffix,
+ backup_duplicate_overwrite,
+ verify_file, download_count))
def add_device(self, path, name, backup_type):
"""
@@ -2343,8 +2356,8 @@ class RapidApp(dbus.service.Object):
self.download_active_by_scan_pid = []
def modify_files_during_download(self):
- """ Returns True if there is a need to modify files during download"""
- return self.prefs.auto_rotate_jpeg or (self.focal_length is not None)
+ """ Returns True if there is a need to modify or verify files during download"""
+ return self.prefs.auto_rotate_jpeg or (self.focal_length is not None) or self.prefs.verify_file
def start_download(self, scan_pid=None):
@@ -2355,6 +2368,7 @@ class RapidApp(dbus.service.Object):
"""
files_by_scan_pid = self.thumbnails.get_files_checked_for_download(scan_pid)
+ self.check_file_types_to_be_downloaded(files_by_scan_pid)
folders_valid, invalid_dirs = self.check_download_folder_validity(files_by_scan_pid)
if not folders_valid:
@@ -2366,6 +2380,18 @@ class RapidApp(dbus.service.Object):
self.log_error(config.CRITICAL_ERROR, _("Download cannot proceed"),
msg)
else:
+ missing_destinations = self.backup_destinations_missing()
+ if missing_destinations is not None:
+ # Warn user that they have specified that they want to backup a file type, but no such folder exists on backup devices
+ if not missing_destinations[0]:
+ logger.warning("No backup device contains a valid folder for backing up photos")
+ msg = _("No backup device contains a valid folder for backing up %(filetype)s") % {'filetype': _('photos')}
+ else:
+ logger.warning("No backup device contains a valid folder for backing up videos")
+ msg = _("No backup device contains a valid folder for backing up %(filetype)s") % {'filetype': _('videos')}
+
+ self.log_error(config.WARNING, _("Backup problem"), msg)
+
# set time download is starting if it is not already set
# it is unset when all downloads are completed
if self.download_start_time is None:
@@ -2457,9 +2483,17 @@ class RapidApp(dbus.service.Object):
for rpd_file in files:
rpd_file.generate_thumbnail = True
+ verify_file = self.prefs.verify_file
+ if verify_file:
+ # since a file might be modified in the file modify process,
+ # if it will be backed up, need to refresh the md5 once it has
+ # been modified
+ refresh_md5_on_file_change = self.prefs.backup_images
+ else:
+ refresh_md5_on_file_change = False
modify_files_during_download = self.modify_files_during_download()
if modify_files_during_download:
- self.file_modify_manager.add_task((scan_pid, self.prefs.auto_rotate_jpeg, self.focal_length))
+ self.file_modify_manager.add_task((scan_pid, self.prefs.auto_rotate_jpeg, self.focal_length, verify_file, refresh_md5_on_file_change))
modify_pipe = self.file_modify_manager.get_modify_pipe(scan_pid)
else:
modify_pipe = None
@@ -2468,7 +2502,8 @@ class RapidApp(dbus.service.Object):
# Initiate copy files process
self.copy_files_manager.add_task((photo_download_folder,
video_download_folder, scan_pid,
- files, modify_files_during_download,
+ files, verify_file,
+ modify_files_during_download,
modify_pipe))
def copy_files_results(self, source, condition):
@@ -2549,11 +2584,13 @@ class RapidApp(dbus.service.Object):
rpd_file.synchronize_raw_jpg = self.prefs.must_synchronize_raw_jpg()
rpd_file.job_code = self.job_code
- self.subfolder_file_manager.rename_file_and_move_to_subfolder(
- download_succeeded,
- download_count,
- rpd_file
- )
+ # Call this even if download did not succeed e.g. file verification error
+ self.subfolder_file_manager.rename_file_and_move_to_subfolder(
+ download_succeeded,
+ download_count,
+ rpd_file
+ )
+
def file_modify_results(self, source, condition):
"""
'file modify' is a process that runs immediately after 'copy files',
@@ -2595,22 +2632,39 @@ class RapidApp(dbus.service.Object):
self.log_error(config.WARNING, rpd_file.error_title,
rpd_file.error_msg, rpd_file.error_extra_detail)
- if self.prefs.backup_images and len(self.backup_devices):
- if self.prefs.backup_device_autodetection:
- if rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO:
- path_suffix = self.prefs.backup_identifier
+ if self.prefs.backup_images:
+ if self.backup_possible(rpd_file.file_type):
+ if self.prefs.backup_device_autodetection:
+ if rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO:
+ path_suffix = self.prefs.backup_identifier
+ else:
+ path_suffix = self.prefs.video_backup_identifier
else:
- path_suffix = self.prefs.video_backup_identifier
+ path_suffix = None
+
+ self.backup_manager.backup_file(move_succeeded, rpd_file,
+ path_suffix,
+ self.prefs.backup_duplicate_overwrite,
+ self.prefs.verify_file,
+ download_count)
else:
- path_suffix = None
+ if rpd_file.status == config.STATUS_DOWNLOAD_FAILED:
+ rpd_file.status = config.STATUS_DOWNLOAD_AND_BACKUP_FAILED
+ else:
+ rpd_file.status = config.STATUS_BACKUP_PROBLEM
- self.backup_manager.backup_file(move_succeeded, rpd_file,
- path_suffix,
- self.prefs.backup_duplicate_overwrite,
- download_count)
+ self.file_download_finished(move_succeeded, rpd_file)
else:
self.file_download_finished(move_succeeded, rpd_file)
+ def backup_possible(self, file_type):
+ if file_type == rpdfile.FILE_TYPE_PHOTO:
+ return self.no_photo_backup_devices > 0
+ elif file_type == rpdfile.FILE_TYPE_VIDEO:
+ return self.no_video_backup_devices > 0
+ else:
+ logger.critical("Unrecognized file type when determining if backup is possible")
+
def multiple_backup_devices(self, file_type):
"""Returns true if more than one backup device is being used for that
@@ -2641,22 +2695,24 @@ class RapidApp(dbus.service.Object):
self.time_remaining.update(scan_pid, bytes_downloaded=chunk_downloaded)
elif msg_type == rpdmp.MSG_FILE:
- backup_succeeded, rpd_file = data
+ backup_succeeded, do_backup, rpd_file = data
# Only show an error message if there is more than one device
# backing up files of this type - if that is the case,
- # do not want to reply on showing an error message in the
+ # do not want to rely on showing an error message in the
# function file_download_finished, as it is only called once,
# when all files have been backed up
- if not backup_succeeded and self.multiple_backup_devices(rpd_file.file_type):
+ if not backup_succeeded and self.multiple_backup_devices(rpd_file.file_type) and do_backup:
self.log_error(config.SERIOUS_ERROR,
rpd_file.error_title,
rpd_file.error_msg, rpd_file.error_extra_detail)
- self.download_tracker.file_backed_up(rpd_file.unique_id)
- if self.download_tracker.all_files_backed_up(rpd_file.unique_id,
+ if do_backup:
+ self.download_tracker.file_backed_up(rpd_file.unique_id)
+ if self.download_tracker.all_files_backed_up(rpd_file.unique_id,
rpd_file.file_type):
- self.file_download_finished(backup_succeeded, rpd_file)
+ logger.debug("File %s will not be backed up to any more locations", rpd_file.download_name)
+ self.file_download_finished(backup_succeeded or not do_backup, rpd_file)
return True
else:
return False
@@ -2901,7 +2957,7 @@ class RapidApp(dbus.service.Object):
files_to_download = self.download_tracker.get_no_files_in_download(scan_pid)
file_types = self.download_tracker.get_file_types_present(scan_pid)
completed = files_downloaded == files_to_download
- if completed and (self.prefs.backup_images and len(self.backup_devices)):
+ if completed and (self.prefs.backup_images and self.backup_possible(file_type)):
completed = self.download_tracker.all_files_backed_up(unique_id, file_type)
if completed:
@@ -3753,6 +3809,22 @@ class RapidApp(dbus.service.Object):
return (photo_size, video_size)
+ def check_file_types_to_be_downloaded(self, files_by_scan_pid):
+ """Determines what types of files need to be downloaded, setting
+ self.downloading_photos and self.downloading_videos accordingly"""
+ self.downloading_photos = False
+ self.downloading_videos = False
+ while not self.downloading_photos and not self.downloading_videos:
+ for scan_pid in files_by_scan_pid:
+ files = files_by_scan_pid[scan_pid]
+ if not self.downloading_photos:
+ if self.files_of_type_present(files, rpdfile.FILE_TYPE_PHOTO):
+ self.downloading_photos = True
+ if not self.downloading_videos:
+ if self.files_of_type_present(files, rpdfile.FILE_TYPE_VIDEO):
+ self.downloading_videos = True
+
+
def check_download_folder_validity(self, files_by_scan_pid):
"""
Checks validity of download folders based on the file types the user
@@ -3763,21 +3835,8 @@ class RapidApp(dbus.service.Object):
"""
valid = True
invalid_dirs = []
- # first, check what needs to be downloaded - photos and / or videos
- need_photo_folder = False
- need_video_folder = False
- while not need_photo_folder and not need_video_folder:
- for scan_pid in files_by_scan_pid:
- files = files_by_scan_pid[scan_pid]
- if not need_photo_folder:
- if self.files_of_type_present(files, rpdfile.FILE_TYPE_PHOTO):
- need_photo_folder = True
- if not need_video_folder:
- if self.files_of_type_present(files, rpdfile.FILE_TYPE_VIDEO):
- need_video_folder = True
- # second, check validity
- if need_photo_folder:
+ if self.downloading_photos:
if not self.is_valid_download_dir(self.prefs.download_folder,
is_photo_dir=True):
valid = False
@@ -3786,7 +3845,7 @@ class RapidApp(dbus.service.Object):
logger.debug("Photo download folder is valid: %s",
self.prefs.download_folder)
- if need_video_folder:
+ if self.downloading_videos:
if not self.is_valid_download_dir(self.prefs.video_download_folder,
is_photo_dir=False):
valid = False
@@ -3798,6 +3857,19 @@ class RapidApp(dbus.service.Object):
return (valid, invalid_dirs)
+ def backup_destinations_missing(self):
+ if self.prefs.backup_images and self.prefs.backup_device_autodetection:
+ photo_backup_ok = video_backup_ok = True
+ if self.downloading_photos and not self.backup_possible(rpdfile.FILE_TYPE_PHOTO):
+ photo_backup_ok = False
+ if self.downloading_videos and not self.backup_possible(rpdfile.FILE_TYPE_VIDEO):
+ video_backup_ok = False
+ if photo_backup_ok and video_backup_ok:
+ return None
+ else:
+ return (photo_backup_ok, video_backup_ok)
+ return None
+
def same_file_system(self, file1, file2):
"""Returns True if the files / diretories are on the same file system
"""