Logo Search packages:      
Sourcecode: rapid-photo-downloader version File versions  Download package

def rapid::rapid::CopyPhotos::run (   self  ) 

Copy photos from device to local drive, and if requested, backup

1.  Should the image be downloaded?
    1.a  generate file name 
1.a.1  generate sequence numbers if needed
1.a.2  FIFO queue sequence numbers to indicate that they could 
          potentially be used in a filename
    1.b  check to see if a file exists with the same name in the place it will 
   be downloaded to
    1.c if it exisits, and unique identifiers are not being used:
1.b.1  if using sequence numbers or letters, then potentially any of the 
          sequence numbers in the queue could be used to make the filename
    1.b.1.a  generate and check each filename using sequence numbers in the queue
    1.b.1.b  if one of these filenames is unique, then image needs to be downloaded
1.b.2  do not do not download


2.  Download the image
    2.a  copy it to temporary folder (this takes time)
    2.b  is the file name still unique? Perhaps a new file was created with this name in the meantime
   (either by another thread or another program)
2.b.1  don't allow any other thread to rename a file
2.b.2  check file name
2.b.3  adding suffix if it is not unique, being careful not to overwrite any existing file with a suffix
2.b.4  rename it to the "real" name, effectively performing a mv
2.b.5  allow other threads to rename files

3.  Backup the image, using the same filename as was used when it was downloaded
    3.a  does a file with the same name already exist on the backup medium?
    3.b  if so, user preferences determine whether it should be overwritten or not

Definition at line 1762 of file rapid.py.

01762                  :
        """
        Copy photos from device to local drive, and if requested, backup
        
        1.  Should the image be downloaded?
            1.a  generate file name 
                1.a.1  generate sequence numbers if needed
                1.a.2  FIFO queue sequence numbers to indicate that they could 
                          potentially be used in a filename
            1.b  check to see if a file exists with the same name in the place it will 
                   be downloaded to
            1.c if it exisits, and unique identifiers are not being used:
                1.b.1  if using sequence numbers or letters, then potentially any of the 
                          sequence numbers in the queue could be used to make the filename
                    1.b.1.a  generate and check each filename using sequence numbers in the queue
                    1.b.1.b  if one of these filenames is unique, then image needs to be downloaded
                1.b.2  do not do not download

        
        2.  Download the image
            2.a  copy it to temporary folder (this takes time)
            2.b  is the file name still unique? Perhaps a new file was created with this name in the meantime
                   (either by another thread or another program)
                2.b.1  don't allow any other thread to rename a file
                2.b.2  check file name
                2.b.3  adding suffix if it is not unique, being careful not to overwrite any existing file with a suffix
                2.b.4  rename it to the "real" name, effectively performing a mv
                2.b.5  allow other threads to rename files
        
        3.  Backup the image, using the same filename as was used when it was downloaded
            3.a  does a file with the same name already exist on the backup medium?
            3.b  if so, user preferences determine whether it should be overwritten or not
        """

        def checkDownloadPath(path):
            """
            Checks to see if download folder exists.
            
            Creates it if it does not exist.
            
            Returns False if the path could not be created.
            """
            
            try:
                if not os.path.isdir(path):
                    os.makedirs(path)
                return True
                    
            except:
                display_queue.put((media_collection_treeview.removeCard,  (self.thread_id, )))
                msg = _("The following download path could not be created:\n")
                msg += _("%(path)s: ") % {'path': path}
                logError(config.CRITICAL_ERROR, _("Download cannot proceed"), msg)
                cmd_line(_("Download cannot proceed"))
                cmd_line(msg)
                display_queue.put((self.parentApp.downloadFailed,  (self.thread_id, )))
                display_queue.close("rw")                     
                return False                
                
        def getPrefs(notifyOnError):
            try:
                self.initializeFromPrefs(notifyOnError)
                return True
            except rn.PrefError:
                if notifyOnError:
                    display_queue.put((media_collection_treeview.removeCard,  (self.thread_id, )))
                    msg = _("There is an error in the program preferences.")
                    msg += _("\nPlease check preferences, restart the program, and try again.")
                    logError(config.CRITICAL_ERROR, _("Download cannot proceed"), msg)
                    cmd_line(_("Download cannot proceed"))
                    cmd_line(msg)
                    display_queue.put((self.parentApp.downloadFailed,  (self.thread_id, )))
                    display_queue.close("rw")                     
                return False
        

                    
        def scanMedia():
            """
            Scans media for photos and videos
            """
                       
            # load images to display for when a thumbnail cannot be extracted or created
            
            if DROP_SHADOW:
                self.photoThumbnail = gtk.gdk.pixbuf_new_from_file(paths.share_dir('glade3/photo_shadow.png'))
                self.videoThumbnail = gtk.gdk.pixbuf_new_from_file(paths.share_dir('glade3/video_shadow.png'))
            else:
                self.photoThumbnail = gtk.gdk.pixbuf_new_from_file(paths.share_dir('glade3/photo.png'))
                self.videoThumbnail = gtk.gdk.pixbuf_new_from_file(paths.share_dir('glade3/video.png'))
                
            imageRenameUsesJobCode = rn.usesJobCode(self.prefs.image_rename)
            imageSubfolderUsesJobCode = rn.usesJobCode(self.prefs.subfolder)
            videoRenameUsesJobCode = rn.usesJobCode(self.prefs.video_rename)
            videoSubfolderUsesJobCode = rn.usesJobCode(self.prefs.video_subfolder)
            
            def loadFileMetadata(mediaFile):
                """
                loads the metadate for the file, and additional information if required
                """
                
                problem = pn.Problem()
                try:
                    mediaFile.loadMetadata()
                except:
                    mediaFile.status = STATUS_CANNOT_DOWNLOAD
                    mediaFile.metadata = None
                    problem.add_problem(None, pn.CANNOT_DOWNLOAD_BAD_METADATA, {'filetype': mediaFile.displayNameCap})
                    mediaFile.problem = problem
                else:
                    # generate sample filename and subfolder
                    if mediaFile.isImage:
                        fallback_date = None
                        subfolderPrefsFactory = self.subfolderPrefsFactory
                        renamePrefsFactory = self.imageRenamePrefsFactory
                        nameUsesJobCode = imageRenameUsesJobCode
                        subfolderUsesJobCode = imageSubfolderUsesJobCode                        
                    else:
                        fallback_date = mediaFile.modificationTime
                        subfolderPrefsFactory = self.videoSubfolderPrefsFactory
                        renamePrefsFactory = self.videoRenamePrefsFactory
                        nameUsesJobCode = videoRenameUsesJobCode
                        subfolderUsesJobCode = videoSubfolderUsesJobCode
                        
                    generateSubfolderAndName(mediaFile, problem, subfolderPrefsFactory, renamePrefsFactory, 
                                            nameUsesJobCode, subfolderUsesJobCode, 
                                            self.prefs.strip_characters, fallback_date)
                    # generate thumbnail
                    mediaFile.generateThumbnail(self.videoTempWorkingDir)
                    
                if mediaFile.thumbnail is None:
                    mediaFile.genericThumbnail = True
                    if mediaFile.isImage:
                        mediaFile.thumbnail = self.photoThumbnail
                    else:
                        mediaFile.thumbnail = self.videoThumbnail
            
            def downloadable(name):
                isImage = media.isImage(name)
                isVideo = media.isVideo(name)
                download = (DOWNLOAD_VIDEO and (isImage or isVideo) or 
                        ((not DOWNLOAD_VIDEO) and isImage))
                return (download, isImage, isVideo)
                
            def addFile(name, path, size, modificationTime, device, volume, isImage):
                #~ if debug_info:
                    #~ cmd_line("Scanning %s" % name)
                    
                if isImage:
                    downloadFolder = self.prefs.download_folder
                else:
                    downloadFolder = self.prefs.video_download_folder
                    
                mediaFile = media.MediaFile(self.thread_id, name, path, size, modificationTime, device, downloadFolder, volume, isImage)
                loadFileMetadata(mediaFile)
                # modificationTime is very useful for quick sorting
                imagesAndVideos.append((mediaFile, modificationTime))
                display_queue.put((self.parentApp.addFile, (mediaFile,)))
                
                if isImage:
                    self.noImages += 1
                else:
                    self.noVideos += 1

            
            def gio_scan(path, fileSizeSum):
                """recursive function to scan a directory and its subdirectories
                for photos and possibly videos"""
                
                children = path.enumerate_children('standard::name,standard::type,standard::size,time::modified')

                for child in children:
                    if not self.running:
                        self.lock.acquire()
                        self.running = True
                    
                    if not self.ctrl:
                        return None
                        
                    if child.get_file_type() == gio.FILE_TYPE_DIRECTORY:
                        fileSizeSum = gio_scan(path.get_child(child.get_name()), fileSizeSum)
                        if fileSizeSum == None:
                            # this value will be None only if the thread is exiting
                            return None
                    elif child.get_file_type() == gio.FILE_TYPE_REGULAR:
                        name = child.get_name()
                        download, isImage, isVideo = downloadable(name)
                        if download:
                            size = child.get_size()
                            modificationTime = child.get_modification_time()
                            addFile(name, path.get_path(), size, modificationTime, self.cardMedia.prettyName(limit=0), self.cardMedia.volume, isImage)
                            fileSizeSum += size

                return fileSizeSum
            
                        
            imagesAndVideos = []
            fileSizeSum = 0
            self.noVideos = 0
            self.noImages = 0
                        
            if not using_gio or not self.cardMedia.volume:
                for root, dirs, files in os.walk(self.cardMedia.getPath()):
                    for name in files:
                        if not self.running:
                            self.lock.acquire()
                            self.running = True
                        
                        if not self.ctrl:
                            return None
                        

                        download, isImage, isVideo = downloadable(name)
                        if download:
                            fullFileName = os.path.join(root, name)
                            size = os.path.getsize(fullFileName)
                            modificationTime = os.path.getmtime(fullFileName)
                            addFile(name, root, size, modificationTime, self.cardMedia.prettyName(limit=0), self.cardMedia.volume, isImage)
                            fileSizeSum += size

                            
            else:
                # using gio and have a volume
                # make call to recursive function to scan volume
                fileSizeSum = gio_scan(self.cardMedia.volume.volume.get_root(), fileSizeSum)
                if fileSizeSum == None:
                    # thread exiting
                    return None
                
            # sort in place based on modification time
            imagesAndVideos.sort(key=operator.itemgetter(1))
            noFiles = len(imagesAndVideos)
            
            self.scanComplete = True
            
            self.display_file_types = file_types_by_number(self.noImages, self.noVideos)

            
            if noFiles:
                self.cardMedia.setMedia(imagesAndVideos, fileSizeSum, noFiles)
                # Translators: as already, mentioned the %s value should not be modified or left out. It may be moved if necessary.
                # It refers to the actual number of photos that can be copied. For example, the user might see the following:
                # '0 of 512 photos' or '0 of 10 videos' or '0 of 202 photos and videos'.
                # This particular text is displayed to the user before the download has started.
                display = _("%(number)s %(filetypes)s") % {'number':noFiles, 'filetypes':self.display_file_types}
                display_queue.put((media_collection_treeview.updateCard, (self.thread_id,  self.cardMedia.sizeOfImagesAndVideos())))
                display_queue.put((media_collection_treeview.updateProgress, (self.thread_id, 0.0, display, 0)))
                display_queue.put((self.parentApp.setDownloadButtonSensitivity, ()))
                
                # Translators: as you have already seen, the text can contain values that should not be modified or left out by you, for example %s.
                # This text is another example of that, but it is is a little more complex. Here there are two values which will be displayed
                # to the user when they run the program, signifying the number of photos found, and the device they were found on.
                # %(number)s should be left exactly as is: 'number' should not be translated. The same applies to %(device)s: 'device' should
                # not be translated. Generally speaking, if translating the sentence requires it, you can move items like '%(xyz)s' around 
                # in a sentence, but you should never modify them or leave them out.
                cmd_line(_("Device scan complete: found %(number)s %(filetypes)s on %(device)s") % 
                           {'number': noFiles, 'filetypes':self.display_file_types,
                            'device': self.cardMedia.prettyName(limit=0)})
                return True
            else:
                # it might be better to display "0 of 0" here
                display_queue.put((media_collection_treeview.removeCard,  (self.thread_id, )))
                cmd_line(_("Device scan complete: no %(filetypes)s found on %(device)s") % {'device':self.cardMedia.prettyName(limit=0), 'filetypes':self.types_searched_for})
                return False
            
            
        def logError(severity, problem, details, resolution=None):
            display_queue.put((log_dialog.addMessage, (self.thread_id, severity, problem, details, 
                            resolution)))
            if severity == config.WARNING:
                self.noWarnings += 1
            else:
                self.noErrors += 1

        def notifyAndUnmount(umountAttemptOK):
            if not self.cardMedia.volume:
                unmountMessage = ""
                notificationName = PROGRAM_NAME
            else:
                notificationName  = self.cardMedia.volume.get_name()
                if self.prefs.auto_unmount and umountAttemptOK:
                    self.cardMedia.volume.unmount(self.on_volume_unmount)
                    # This message informs the user that the device (e.g. camera, hard drive or memory card) was automatically unmounted and they can now remove it
                    unmountMessage = _("The device can now be safely removed")
                else:
                    unmountMessage = ""
            
            file_types = file_types_by_number(noImagesDownloaded, noVideosDownloaded)
            file_types_skipped = file_types_by_number(noImagesSkipped, noVideosSkipped)
            message = _("%(noFiles)s %(filetypes)s downloaded") % {'noFiles':noFilesDownloaded, 'filetypes': file_types}
            noFilesSkipped = noImagesSkipped + noVideosSkipped
            if noFilesSkipped:
                message += "\n" + _("%(noFiles)s %(filetypes)s failed to download") % {'noFiles':noFilesSkipped, 'filetypes':file_types_skipped}
            
            if self.noWarnings:
                message = "%s\n%s " % (message,  self.noWarnings) + _("warnings") 
            if self.noErrors:
                message = "%s\n%s " % (message,  self.noErrors) + _("errors")
                
            if unmountMessage:
                message = "%s\n%s" % (message,  unmountMessage)                
                
            n = pynotify.Notification(notificationName,  message)
            
            if self.cardMedia.volume:
                icon = self.cardMedia.volume.get_icon_pixbuf(self.parentApp.notification_icon_size)
            else:
                icon = self.parentApp.application_icon
            
            n.set_icon_from_pixbuf(icon)
            n.show()            

        def createTempDir(baseDir):
            """
            Create a temporary directory in which to download the photos to.
            
            Returns the directory if it was created, else returns None.
            
            Don't want to put it in system temp folder, as that is likely
            to be on another partition and hence copying files from it
            to the actual download folder will be slow!"""
            try:
                t = tempfile.mkdtemp(prefix='rapid-tmp-', 
                                                dir=baseDir)
                return t
            except OSError, (errno, strerror):
                if not self.cardMedia.volume:
                    image_device = _("Source: %s\n") % self.cardMedia.getPath()
                else:
                    _("Device: %s\n") % self.cardMedia.volume.get_name()
                destination = _("Destination: %s") % baseDir
                logError(config.CRITICAL_ERROR, _('Could not create temporary download directory'), 
                             image_device + destination,
                            _("Download cannot proceed"))
                cmd_line(_("Error:") + " " + _('Could not create temporary download directory'))
                cmd_line(image_device + destination)
                cmd_line(_("Download cannot proceed"))
                display_queue.put((media_collection_treeview.removeCard,  (self.thread_id, )))
                display_queue.put((self.parentApp.downloadFailed,  (self.thread_id, )))
                display_queue.close("rw")
                self.running = False
                self.lock.release()
                return None      
            
        def setupBackup():
            """
            Check for presence of backup path or volumes, and return the number of devices being used (1 in case of a path)
            """
            no_devices = 0
            if self.prefs.backup_images:
                no_devices = len(self.parentApp.backupVolumes)          
                if not self.prefs.backup_device_autodetection:
                    if not os.path.isdir(self.prefs.backup_location):
                        # the user has manually specified a path, but it
                        # does not exist. This is a problem.
                        try:
                            os.makedirs(self.prefs.backup_location)
                        except:
                            logError(config.SERIOUS_ERROR, _("Backup path does not exist"),
                                        _("The path %s could not be created") % path, 
                                        _("No backups can occur")
                                    )
                            no_devices = 0
            return no_devices
        
        def checkIfNeedAJobCode():
            needAJobCode = NeedAJobCode(self.prefs)
            
            for f in self.cardMedia.imagesAndVideos:
                mediaFile = f[0]
                if mediaFile.status in [STATUS_WARNING, STATUS_NOT_DOWNLOADED]:
                    if needAJobCode.needAJobCode(mediaFile.jobcode, mediaFile.isImage):
                        return True
            return False
            
        def createBothTempDirs():
            self.photoTempWorkingDir = createTempDir(photoBaseDownloadDir)
            created = self.photoTempWorkingDir is not None
            if created and DOWNLOAD_VIDEO:
                self.videoTempWorkingDir = createTempDir(videoBaseDownloadDir)
                created = self.videoTempWorkingDir is not None
                
            return created


        def checkProblemWithNameGeneration(mediaFile):
            if mediaFile.problem.has_problem():
                logError(config.WARNING, 
                    mediaFile.problem.get_title(),
                    _("Source: %(source)s\nDestination: %(destination)s\n%(problem)s") % 
                    {'source': mediaFile.fullFileName, 'destination': mediaFile.downloadFullFileName, 'problem': mediaFile.problem.get_problems()})
                mediaFile.status = STATUS_DOWNLOADED_WITH_WARNING
                    
        def fileAlreadyExists(mediaFile, identifier=None):
            """ Notify the user that the photo or video could not be downloaded because it already exists"""
            
            # get information on when the existing file was last modified
            try:
                modificationTime = os.path.getmtime(mediaFile.downloadFullFileName)
                dt = datetime.datetime.fromtimestamp(modificationTime)
                date = dt.strftime("%x")
                time = dt.strftime("%X")
            except:
                sys.stderr.write("WARNING: could not determine the file modification time of an existing file\n")
                date = time = ''
                
            if not identifier:
                mediaFile.problem.add_problem(None, pn.FILE_ALREADY_EXISTS_NO_DOWNLOAD, {'filetype':mediaFile.displayNameCap})
                mediaFile.problem.add_extra_detail(pn.EXISTING_FILE, {'filetype': mediaFile.displayName, 'date': date, 'time': time})
                mediaFile.status = STATUS_DOWNLOAD_FAILED
                log_status = config.SERIOUS_ERROR
                problem_text = pn.extra_detail_definitions[pn.EXISTING_FILE] % {'date':date, 'time':time, 'filetype': mediaFile.displayName}
            else:
                mediaFile.problem.add_problem(None, pn.UNIQUE_IDENTIFIER_ADDED, {'filetype':mediaFile.displayNameCap})
                mediaFile.problem.add_extra_detail(pn.UNIQUE_IDENTIFIER, {'identifier': identifier, 'filetype': mediaFile.displayName, 'date': date, 'time': time})
                mediaFile.status = STATUS_DOWNLOADED_WITH_WARNING
                log_status = config.WARNING
                problem_text = pn.extra_detail_definitions[pn.UNIQUE_IDENTIFIER] % {'identifier': identifier, 'filetype': mediaFile.displayName, 'date': date, 'time': time}
                
            logError(log_status, mediaFile.problem.get_title(),
                _("Source: %(source)s\nDestination: %(destination)s")
                % {'source': mediaFile.fullFileName, 'destination': mediaFile.downloadFullFileName},
                problem_text)

        def downloadCopyingError(mediaFile, inst=None, errno=None, strerror=None):
            """Notify the user that an error occurred (most likely at the OS / filesystem level) when coyping a photo or video"""
            
            if errno != None and strerror != None:
                mediaFile.problem.add_problem(None, pn.DOWNLOAD_COPYING_ERROR_W_NO, {'filetype': mediaFile.displayName})
                mediaFile.problem.add_extra_detail(pn.DOWNLOAD_COPYING_ERROR_W_NO_DETAIL, {'errorno': errno, 'strerror': strerror})

            else:
                mediaFile.problem.add_problem(None, pn.DOWNLOAD_COPYING_ERROR, {'filetype': mediaFile.displayName})
                if not inst:
                    # hopefully inst will never be None, but just to be safe...
                    inst = _("Please check your system and try again.") 
                mediaFile.problem.add_extra_detail(pn.DOWNLOAD_COPYING_ERROR_DETAIL, inst)

            logError(config.SERIOUS_ERROR, mediaFile.problem.get_title(), mediaFile.problem.get_problems())
            mediaFile.status = STATUS_DOWNLOAD_FAILED
                
        def sameNameDifferentExif(image_name, mediaFile):
            """Notify the user that a file was already downloaded with the same name, but the exif information was different"""
            i1_ext, i1_date_time, i1_subseconds = downloaded_files.extExifDateTime(image_name)
            detail = {'image1': "%s%s" % (image_name, i1_ext), 
                'image1_date': i1_date_time.strftime("%x"),
                'image1_time': time_subseconds_human_readable(i1_date_time, i1_subseconds), 
                'image2':      mediaFile.name, 
                'image2_date': mediaFile.metadata.dateTime().strftime("%x"),
                'image2_time': time_subseconds_human_readable(
                                    mediaFile.metadata.dateTime(), 
                                    mediaFile.metadata.subSeconds())}
            mediaFile.problem.add_problem(None, pn.SAME_FILE_DIFFERENT_EXIF, detail)

            msg = pn.problem_definitions[pn.SAME_FILE_DIFFERENT_EXIF][1] % detail
            logError(config.WARNING,_('Photos detected with the same filenames, but taken at different times'), msg)
            mediaFile.status = STATUS_DOWNLOADED_WITH_WARNING

        def generateSubfolderAndFileName(mediaFile):
            """
            Generates subfolder and file names for photos and videos
            """
            
            skipFile = alreadyDownloaded = False
            sequence_to_use = None
            
            if mediaFile.isVideo:
                fileRenameFactory = self.videoRenamePrefsFactory
                subfolderFactory = self.videoSubfolderPrefsFactory
            else:
                # file is an photo
                fileRenameFactory = self.imageRenamePrefsFactory                
                subfolderFactory = self.subfolderPrefsFactory
            
            fileRenameFactory.setJobCode(mediaFile.jobcode)
            subfolderFactory.setJobCode(mediaFile.jobcode)

            mediaFile.problem = pn.Problem()
            subfolderFactory.initializeProblem(mediaFile.problem)
            fileRenameFactory.initializeProblem(mediaFile.problem)
            
            # Here we cannot assume that the subfolder value will contain something -- the user may have changed the preferences after the scan
            mediaFile.downloadSubfolder = subfolderFactory.generateNameUsingPreferences(
                                                    mediaFile.metadata, mediaFile.name, 
                                                    self.stripCharacters, fallback_date = mediaFile.modificationTime)


            if self.prefs.synchronize_raw_jpg and usesImageSequenceElements and mediaFile.isImage:
                #synchronizing RAW and JPEG only applies to photos, not videos
                image_name, image_ext = os.path.splitext(mediaFile.name)
                with self.downloadedFilesLock:
                    i, sequence_to_use = downloaded_files.matching_pair(image_name, image_ext, mediaFile.metadata.dateTime(), mediaFile.metadata.subSeconds())
                    if i == -1:
                        # this exact file has already been downloaded (same extension, same filename, and same exif date time subsecond info)
                        if not addUniqueIdentifier:
                            logError(config.SERIOUS_ERROR,_('Photo has already been downloaded'), 
                                        _("Source: %(source)s") % {'source': mediaFile.fullFileName})
                            mediaFile.problem.add_problem(None, pn.FILE_ALREADY_DOWNLOADED, {'filetype': mediaFile.displayNameCap})
                            skipFile = True
                            
                
            # pass the subfolder the image will go into, as this is needed to determine subfolder sequence numbers 
            # indicate that sequences chosen should be queued
            
            if not skipFile:
                mediaFile.downloadName = fileRenameFactory.generateNameUsingPreferences(
                                                            mediaFile.metadata, mediaFile.name, self.stripCharacters,  mediaFile.downloadSubfolder,  
                                                            sequencesPreliminary = True,
                                                            sequence_to_use = sequence_to_use,
                                                            fallback_date = mediaFile.modificationTime)

                mediaFile.downloadPath = os.path.join(mediaFile.downloadFolder, mediaFile.downloadSubfolder)
                mediaFile.downloadFullFileName = os.path.join(mediaFile.downloadPath, mediaFile.downloadName)
                    
                if not mediaFile.downloadName or not mediaFile.downloadSubfolder:
                    if not mediaFile.downloadName and not mediaFile.downloadSubfolder:
                        area = _("subfolder and filename")
                    elif not mediaFile.downloadName:
                        area = _("filename")
                    else:
                        area = _("subfolder")
                    problem.add_problem(None, pn.ERROR_IN_NAME_GENERATION, {'filetype': mediaFile.displayNameCap, 'area': area})
                    problem.add_extra_detail(pn.NO_DATA_TO_NAME, {'filetype': area})
                    skipFile = True
                    logError(config.SERIOUS_ERROR, pn.problem_definitions[ERROR_IN_NAME_GENERATION][1] % {'filetype': mediaFile.displayNameCap, 'area': area})
            
            if not skipFile:
                checkProblemWithNameGeneration(mediaFile)
            else:
                self.sizeDownloaded += mediaFile.size * (no_backup_devices + 1)
                mediaFile.status = STATUS_DOWNLOAD_FAILED
                
            return (skipFile, sequence_to_use)
        
        def progress_callback(amount_downloaded, total):
            if (amount_downloaded - self.bytes_downloaded > 2097152) or (amount_downloaded == total):
                chunk_downloaded = amount_downloaded - self.bytes_downloaded
                self.bytes_downloaded = amount_downloaded
                percentComplete = (float(self.sizeDownloaded + amount_downloaded) / sizeFiles) * 100

                display_queue.put((media_collection_treeview.updateProgress, (self.thread_id, percentComplete, None, chunk_downloaded)))        
        
        def downloadFile(mediaFile, sequence_to_use):
            """
            Downloads the photo or video file to the specified subfolder 
            """
            
            if not mediaFile.isImage:
                renameFactory = self.videoRenamePrefsFactory
            else:
                renameFactory = self.imageRenamePrefsFactory
            
            def progress_callback_no_update(amount_downloaded, total):
                pass
                
            try:
                fileDownloaded = False
                if not os.path.isdir(mediaFile.downloadPath):
                    os.makedirs(mediaFile.downloadPath)
                
                nameUniqueBeforeCopy = True
                downloadNonUniqueFile = True
                
                # do a preliminary check to see if a file with the same name already exists
                if os.path.exists(mediaFile.downloadFullFileName):
                    nameUniqueBeforeCopy = False
                    if not addUniqueIdentifier:
                        downloadNonUniqueFile = False
                        if (usesVideoSequenceElements and not mediaFile.isImage) or (usesImageSequenceElements and mediaFile.isImage and not self.prefs.synchronize_raw_jpg):
                            # potentially, a unique file name could still be generated
                            # investigate this possibility
                            with self.fileSequenceLock:
                                for possibleName in renameFactory.generateNameSequencePossibilities(
                                                        mediaFile.metadata, 
                                                        mediaFile.name, self.stripCharacters, mediaFile.downloadSubfolder):
                                    if possibleName:
                                        # no need to check for any problems here, it's just a temporary name
                                        possibleFile = os.path.join(mediaFile.downloadPath, possibleName)
                                        possibleTempFile = os.path.join(tempWorkingDir, possibleName)
                                        if not os.path.exists(possibleFile) and not os.path.exists(possibleTempFile):
                                            downloadNonUniqueFile = True
                                            break

                                        
                    if not downloadNonUniqueFile:
                        fileAlreadyExists(mediaFile)

                copy_succeeded = False
                if nameUniqueBeforeCopy or downloadNonUniqueFile:
                    tempWorkingfile = os.path.join(tempWorkingDir, mediaFile.downloadName)
                    if using_gio:
                        g_dest = gio.File(path=tempWorkingfile)
                        g_src = gio.File(path=mediaFile.fullFileName)
                        try:
                            if not g_src.copy(g_dest, progress_callback, cancellable=gio.Cancellable()):
                                downloadCopyingError(mediaFile)
                            else:
                                copy_succeeded = True
                        except glib.GError, inst:
                            downloadCopyingError(mediaFile, inst=inst)
                    else:
                        shutil.copy2(mediaFile.fullFileName, tempWorkingfile)
                        copy_succeeded = True
                    
                    if copy_succeeded:
                        with self.fileRenameLock:
                            doRename = True
                            if usesSequenceElements:
                                with self.fileSequenceLock:
                                    # get a filename and use this as the "real" filename
                                    if sequence_to_use is None and self.prefs.synchronize_raw_jpg and mediaFile.isImage:
                                        # must check again, just in case the matching pair has been downloaded in the meantime
                                        image_name, image_ext = os.path.splitext(mediaFile.name)
                                        with self.downloadedFilesLock:
                                            i, sequence_to_use = downloaded_files.matching_pair(image_name, image_ext, mediaFile.metadata.dateTime(), mediaFile.metadata.subSeconds())
                                            if i == -99:
                                                sameNameDifferentExif(image_name, mediaFile)

                                    mediaFile.downloadName = renameFactory.generateNameUsingPreferences(
                                                                    mediaFile.metadata, mediaFile.name, self.stripCharacters, mediaFile.downloadSubfolder,  
                                                                    sequencesPreliminary = False,
                                                                    sequence_to_use = sequence_to_use,
                                                                    fallback_date = mediaFile.modificationTime)
                                                                    
                                if not mediaFile.downloadName:
                                    # there was a serious error generating the filename
                                    doRename = False                            
                                else:
                                    mediaFile.downloadFullFileName = os.path.join(mediaFile.downloadPath, mediaFile.downloadName)
                            # check if the file exists again
                            if os.path.exists(mediaFile.downloadFullFileName):
                                if not addUniqueIdentifier:
                                    doRename = False
                                    fileAlreadyExists(mediaFile)
                                else:
                                    # add  basic suffix to make the filename unique
                                    name = os.path.splitext(mediaFile.downloadName)
                                    suffixAlreadyUsed = True
                                    while suffixAlreadyUsed:
                                        if mediaFile.downloadFullFileName in duplicate_files:
                                            duplicate_files[mediaFile.downloadFullFileName] +=  1
                                        else:
                                            duplicate_files[mediaFile.downloadFullFileName] = 1
                                        identifier = '_%s' % duplicate_files[mediaFile.downloadFullFileName]
                                        mediaFile.downloadName = name[0] + identifier + name[1]
                                        possibleNewFile = os.path.join(mediaFile.downloadPath, mediaFile.downloadName)
                                        suffixAlreadyUsed = os.path.exists(possibleNewFile)

                                    fileAlreadyExists(mediaFile, identifier)
                                    mediaFile.downloadFullFileName = possibleNewFile
                                    

                            if doRename:
                                rename_succeeded = False
                                if using_gio:
                                    g_dest = gio.File(path=mediaFile.downloadFullFileName)
                                    g_src = gio.File(path=tempWorkingfile)
                                    try:
                                        if not g_src.move(g_dest, progress_callback_no_update, cancellable=gio.Cancellable()):
                                            downloadCopyingError(mediaFile)
                                        else:
                                            rename_succeeded = True
                                    except glib.GError, inst:
                                        downloadCopyingError(mediaFile, inst=inst)
                                else:
                                    os.rename(tempWorkingfile, mediaFile.downloadFullFileName)
                                    rename_succeeded = True
                                        
                                if rename_succeeded:
                                    fileDownloaded = True
                                    if mediaFile.status != STATUS_DOWNLOADED_WITH_WARNING:
                                        mediaFile.status = STATUS_DOWNLOADED
                                    if usesImageSequenceElements:
                                        if self.prefs.synchronize_raw_jpg and mediaFile.isImage:
                                            name, ext = os.path.splitext(mediaFile.name)
                                            if sequence_to_use is None:
                                                with self.fileSequenceLock:
                                                    seq = renameFactory.sequences.getFinalSequence()
                                            else:
                                                seq = sequence_to_use
                                            with self.downloadedFilesLock:
                                                downloaded_files.add_download(name, ext, mediaFile.metadata.dateTime(), mediaFile.metadata.subSeconds(), seq) 

                                        
                                        with self.fileSequenceLock:
                                            if sequence_to_use is None:
                                                renameFactory.sequences.imageCopySucceeded()
                                                if usesStoredSequenceNo:
                                                    self.prefs.stored_sequence_no += 1
                                                
                                    with self.fileSequenceLock:
                                        if sequence_to_use is None:
                                            if self.prefs.incrementDownloadsToday():
                                                # A new day, according the user's preferences of what time a day begins, has started
                                                cmd_line(_("New day has started - resetting 'Downloads Today' sequence number"))
                                                
                                                sequences.setDownloadsToday(0)
                    
            except (IOError, OSError), (errno, strerror):
                downloadCopyingError(mediaFile, errno=errno, strerror=strerror)              
            
            if usesSequenceElements:
                if not fileDownloaded and sequence_to_use is None:
                    renameFactory.sequences.imageCopyFailed()
            
            #update record keeping using in tracking progress
            self.sizeDownloaded += mediaFile.size
            self.bytes_downloaded_in_download = self.bytes_downloaded
            
            return fileDownloaded
            

        def backupFile(mediaFile, fileDownloaded, no_backup_devices):
            """ 
            Backup photo or video to path(s) chosen by the user
            
            there are three scenarios: 
            (1) file has just been downloaded and should now be backed up
            (2) file was already downloaded on some previous occassion and should still be backed up, because it hasn't been yet
            (3) file has been backed up already (or at least, a file with the same name already exists)
            
            A backup medium can be used to backup photos or videos, or both. 
            """

            backed_up = False
            fileNotBackedUpMessageDisplayed = False
            error_encountered = False
            expected_bytes_downloaded = self.sizeDownloaded + no_backup_devices * mediaFile.size
            
            if no_backup_devices:
                for rootBackupDir in self.parentApp.backupVolumes:
                    self.bytes_downloaded = 0
                    if self.prefs.backup_device_autodetection:
                        volume = self.parentApp.backupVolumes[rootBackupDir].get_name()
                        if mediaFile.isImage:
                            backupDir = os.path.join(rootBackupDir, self.prefs.backup_identifier)
                        else:
                            backupDir = os.path.join(rootBackupDir, self.prefs.video_backup_identifier)
                    else:
                        # photos and videos will be backed up into the same root folder, which the user has manually specified
                        backupDir = rootBackupDir
                        volume = backupDir # os.path.split(backupDir)[1]
                                                
                    # if user has chosen auto detection, then:
                    # photos should only be backed up to photo backup locations
                    # videos should only be backed up to video backup locations
                    # if user did not choose autodetection, and the backup path doesn't exist, then
                    # will try to create it
                    if os.path.isdir(backupDir) or not self.prefs.backup_device_autodetection:

                        backupPath = os.path.join(backupDir, mediaFile.downloadSubfolder)
                        newBackupFile = os.path.join(backupPath, mediaFile.downloadName)
                        copyBackup = True
                        if os.path.exists(newBackupFile):
                            # this check is of course not thread safe -- it doesn't need to be, because at this stage the file names are going to be unique
                            # (the folder structure is the same as the actual download folders, and the file names are unique in them)
                            copyBackup = self.prefs.backup_duplicate_overwrite  
                            
                            if copyBackup:
                                mediaFile.problem.add_problem(None, pn.BACKUP_EXISTS_OVERWRITTEN, volume)
                            else:
                                mediaFile.problem.add_problem(None, pn.BACKUP_EXISTS, volume)
                            severity = config.SERIOUS_ERROR
                            fileNotBackedUpMessageDisplayed = True

                            title = _("Backup of %(file_type)s already exists") % {'file_type': mediaFile.displayName}
                            details = _("Source: %(source)s\nDestination: %(destination)s") \
                                    % {'source': mediaFile.fullFileName, 'destination': newBackupFile}
                            if copyBackup:
                                resolution = _("Backup %(file_type)s overwritten") % {'file_type': mediaFile.displayName}
                            else:
                                if self.prefs.backup_device_autodetection:
                                    volume = self.parentApp.backupVolumes[rootBackupDir].get_name()
                                    resolution = _("%(file_type)s not backed up to %(volume)s") % {'file_type': mediaFile.displayNameCap, 'volume': volume}
                                else:
                                    resolution = _("%(file_type)s not backed up") % {'file_type': mediaFile.displayNameCap}                                
                            logError(severity, title, details, resolution)

                        if copyBackup:
                            if fileDownloaded:
                                fileToCopy = mediaFile.downloadFullFileName
                            else:
                                fileToCopy = mediaFile.fullFileName
                            if os.path.isdir(backupPath):
                                pathExists = True
                            else:
                                pathExists = False
                                # create the backup subfolders
                                if using_gio:
                                    dirs = gio.File(backupPath)
                                    try:
                                        if dirs.make_directory_with_parents(cancellable=gio.Cancellable()):
                                            pathExists = True
                                    except glib.GError, inst:
                                        fileNotBackedUpMessageDisplayed = True
                                        mediaFile.problem.add_problem(None, pn.BACKUP_DIRECTORY_CREATION, volume)
                                        mediaFile.problem.add_extra_detail('%s%s' % (pn.BACKUP_DIRECTORY_CREATION, volume), inst)
                                        error_encountered = True
                                        logError(config.SERIOUS_ERROR, _('Backing up error'), 
                                                 _("Destination directory could not be created: %(directory)s\n") %
                                                 {'directory': backupPath,  } +
                                                 _("Source: %(source)s\nDestination: %(destination)s") % 
                                                 {'source': mediaFile.fullFileName, 'destination': newBackupFile} + "\n" +
                                                 _("Error: %(inst)s") % {'inst': inst}, 
                                                 _('The %(file_type)s was not backed up.') % {'file_type': mediaFile.displayName}
                                                 )                                        
                                else:
                                    # recreate folder structure in backup location
                                    # cannot do os.makedirs(backupPath) - it can give bad results when using external drives
                                    # we know backupDir exists 
                                    # all the components of subfolder may not
                                    folders = mediaFile.downloadSubfolder.split(os.path.sep)
                                    folderToMake = backupDir 
                                    for f in folders:
                                        if f:
                                            folderToMake = os.path.join(folderToMake,  f)
                                            if not os.path.isdir(folderToMake):
                                                try:
                                                    os.mkdir(folderToMake)
                                                    pathExists = True
                                                except (IOError, OSError), (errno, strerror):
                                                    fileNotBackedUpMessageDisplayed = True
                                                    inst = "%s: %s" % (errno, strerror)
                                                    mediaFile.problem.add_problem(None, pn.BACKUP_DIRECTORY_CREATION, volume)
                                                    mediaFile.problem.add_extra_detail('%s%s' % (pn.BACKUP_DIRECTORY_CREATION, volume), inst)
                                                    error_encountered = True
                                                    logError(config.SERIOUS_ERROR, _('Backing up error'), 
                                                             _("Destination directory could not be created: %(directory)s\n") %
                                                             {'directory': backupPath,  } +
                                                             _("Source: %(source)s\nDestination: %(destination)s") % 
                                                             {'source': mediaFile.fullFileName, 'destination': newBackupFile} + "\n" +
                                                             _("Error: %(errno)s %(strerror)s") % {'errno': errno,  'strerror': strerror}, 
                                                             _('The %(file_type)s was not backed up.') % {'file_type': mediaFile.displayName}
                                                             )

                                                    break
                                        
                            if pathExists:
                                if using_gio:
                                    g_dest = gio.File(path=newBackupFile)
                                    g_src = gio.File(path=fileToCopy)
                                    if self.prefs.backup_duplicate_overwrite:
                                        flags = gio.FILE_COPY_OVERWRITE
                                    else:
                                        flags = gio.FILE_COPY_NONE
                                    try:
                                        if not g_src.copy(g_dest, progress_callback, flags, cancellable=gio.Cancellable()):
                                            fileNotBackedUpMessageDisplayed = True
                                            mediaFile.problem.add_problem(None, pn.BACKUP_ERROR, volume)
                                            error_encountered = True
                                        else:
                                            backed_up = True
                                            if mediaFile.status == STATUS_DOWNLOAD_FAILED:
                                                mediaFile.problem.add_problem(None, pn.NO_DOWNLOAD_WAS_BACKED_UP, volume)
                                    except glib.GError, inst:
                                        fileNotBackedUpMessageDisplayed = True
                                        mediaFile.problem.add_problem(None, pn.BACKUP_ERROR, volume)
                                        mediaFile.problem.add_extra_detail('%s%s' % (pn.BACKUP_ERROR, volume), inst)
                                        error_encountered = True
                                        logError(config.SERIOUS_ERROR, _('Backing up error'), 
                                                _("Source: %(source)s\nDestination: %(destination)s") %
                                                 {'source': fileToCopy, 'destination': newBackupFile} + "\n" +
                                                _("Error: %(inst)s") % {'inst': inst},
                                                _('The %(file_type)s was not backed up.')  % {'file_type': mediaFile.displayName}
                                            )
                                else:
                                    try:
                                        shutil.copy2(fileToCopy, newBackupFile)
                                        backed_up = True
                                        if mediaFile.status == STATUS_DOWNLOAD_FAILED:
                                            mediaFile.problem.add_problem(None, pn.NO_DOWNLOAD_WAS_BACKED_UP, volume)
                                        
                                    except (IOError, OSError), (errno, strerror):
                                        fileNotBackedUpMessageDisplayed = True
                                        mediaFile.problem.add_problem(None, pn.BACKUP_ERROR, volume)
                                        inst = "%s: %s" % (errno, strerror)
                                        mediaFile.problem.add_extra_detail('%s%s' % (pn.BACKUP_ERROR, volume), inst)
                                        error_encountered = True
                                        logError(config.SERIOUS_ERROR, _('Backing up error'), 
                                                _("Source: %(source)s\nDestination: %(destination)s") % 
                                                 {'source': fileToCopy, 'destination': newBackupFile} + "\n" +
                                                _("Error: %(errno)s %(strerror)s") % {'errno': errno,  'strerror': strerror},
                                                _('The %(file_type)s was not backed up.')  % {'file_type': mediaFile.displayName}
                                            )
                    
                    #update record keeping using in tracking progress
                    self.sizeDownloaded += mediaFile.size
                    self.bytes_downloaded_in_backup += self.bytes_downloaded

            if not backed_up and not fileNotBackedUpMessageDisplayed:
                # The file has not been backed up to any medium
                mediaFile.problem.add_problem(None, pn.NO_BACKUP_PERFORMED, {'filetype': mediaFile.displayNameCap})

                severity = config.SERIOUS_ERROR
                problem = _("%(file_type)s could not be backed up") % {'file_type': mediaFile.displayName}
                details = _("Source: %(source)s") % {'source': mediaFile.fullFileName}
                if self.prefs.backup_device_autodetection:
                    resolution = _("No suitable backup volume was found")
                else:
                    resolution = _("A backup location was not found")
                logError(severity, problem, details, resolution)    

            if backed_up and mediaFile.status == STATUS_DOWNLOAD_FAILED:
                mediaFile.problem.add_extra_detail(pn.BACKUP_OK_TYPE, mediaFile.displayNameCap)
            
            if not backed_up:
                if mediaFile.status == STATUS_DOWNLOAD_FAILED:
                    mediaFile.status = STATUS_DOWNLOAD_AND_BACKUP_FAILED
                else:
                    mediaFile.status = STATUS_BACKUP_PROBLEM
            elif error_encountered:
                # it was backed up to at least one volume, but there was an error on another backup volume
                if mediaFile.status != STATUS_DOWNLOAD_FAILED:
                    mediaFile.status = STATUS_BACKUP_PROBLEM
            
            # Take into account instances where a backup device has been removed part way through a download
            # (thereby making self.parentApp.backupVolumes have less items than expected)
            if self.sizeDownloaded < expected_bytes_downloaded:
                self.sizeDownloaded = expected_bytes_downloaded
            return backed_up

        
        self.hasStarted = True
        display_queue.open('w')

        #Do not try to handle any preference errors here
        getPrefs(False)
        
        #Check photo and video download path, create if necessary
        photoBaseDownloadDir = self.prefs.download_folder
        if not checkDownloadPath(photoBaseDownloadDir):
            return # cleanup already done

        if DOWNLOAD_VIDEO:
            videoBaseDownloadDir = self.prefs.video_download_folder
            if not checkDownloadPath(videoBaseDownloadDir):
                return
        else:
            videoBaseDownloadDir = self.videoTempWorkingDir = None
            
        if not createBothTempDirs():
            return 
        
        s = scanMedia()
        if s is None:
            if not self.ctrl:
                self.running = False
                display_queue.put((media_collection_treeview.removeCard, (self.thread_id, )))
                display_queue.close("rw")
                return
            else:
                sys.stderr.write("FIXME: scan returned None, but the thread is not meant to be exiting\n")
        if not s:
            cmd_line(_("This device has no %(types_searched_for)s to download from.") % {'types_searched_for': self.types_searched_for})
            display_queue.put((self.parentApp.downloadFailed, (self.thread_id, )))
            display_queue.close("rw")
            self.running = False
            return
        
        if self.scanResultsStale or self.scanResultsStaleDownloadFolder:
            display_queue.put((self.parentApp.regenerateScannedDevices, (self.thread_id, )))
        all_files_downloaded = False
        
        totalNonErrorFiles = self.cardMedia.numberOfFilesNotCannotDownload()
        
        if not self.autoStart:
            # halt thread, waiting to be restarted so download proceeds
            self.cleanUp()
            self.running = False
            self.lock.acquire()

            if not self.ctrl:
                # thread will restart at this point, when the program is exiting
                # so must exit if self.ctrl indicates this

                self.running = False
                display_queue.close("rw")
                return

            self.running = True
            if not createBothTempDirs():
                return
                
        else:
            if need_job_code_for_renaming:
                if checkIfNeedAJobCode():
                    if job_code == None:
                        self.cleanUp()
                        self.waitingForJobCode = True
                        display_queue.put((self.parentApp.getJobCode, ()))
                        self.running = False
                        self.lock.acquire()

                        if not self.ctrl:
                            # thread is exiting
                            display_queue.close("rw")
                            return

                        self.running = True                        
                        self.waitingForJobCode = False
                        if not createBothTempDirs():
                            return
                    else:
                        # User has entered a job code, and it's in the global variable
                        # Assign it to all those files that do not have one
                        display_queue.put((self.parentApp.selection_vbox.selection_treeview.apply_job_code, (job_code, False, True, self.thread_id)))

            # auto start could be false if the user hit cancel when prompted for a job code
            if self.autoStart:
                # set all in this thread to download pending
                display_queue.put((self.parentApp.selection_vbox.selection_treeview.set_status_to_download_pending, (False, self.thread_id)))
                # wait until all the files have had their status set to download pending, and once that is done, restart
                self.running = False
                self.lock.acquire()
                self.running = True
                
                # set download started time
                display_queue.put((self.parentApp.setDownloadStartTime, ()))

        while not all_files_downloaded:

            # set the download start time to be the time that the user clicked the download button, or if on auto start, the value just set
            i = 0
            while self.parentApp.download_start_time is None or i > 2:
                time.sleep(0.5)
                i += 1
            
            if self.parentApp.download_start_time:
                start_time = self.parentApp.download_start_time
            else:
                # in a bizarre corner case situation, with mulitple cards of greatly varying size, 
                # it's possible the start time was set above and then in the meantime unset (very unlikely, but conceivably it could happen)
                # fall back to the current time in this less than satisfactory situation
                start_time = datetime.datetime.now()
                
            self.imageRenamePrefsFactory.setDownloadStartTime(start_time)
            self.subfolderPrefsFactory.setDownloadStartTime(start_time)
            if DOWNLOAD_VIDEO:
                self.videoRenamePrefsFactory.setDownloadStartTime(start_time)
                self.videoSubfolderPrefsFactory.setDownloadStartTime(start_time)
            
            self.noErrors = self.noWarnings = 0
            
            if not getPrefs(True):
                    self.running = False
                    display_queue.close("rw")           
                    return
             
            self.downloadStarted = True
            cmd_line(_("Download has started from %s") % self.cardMedia.prettyName(limit=0))
            
            
            noFiles, sizeFiles, fileIndex = self.cardMedia.sizeAndNumberDownloadPending()
            cmd_line(_("Attempting to download %s files") % noFiles)
                        
            no_backup_devices = setupBackup()

            # include the time it takes to copy to the backup volumes
            sizeFiles = sizeFiles * (no_backup_devices + 1)
            
            display_queue.put((self.parentApp.timeRemaining.set, (self.thread_id, sizeFiles)))
            
            i = 0
            self.sizeDownloaded = noFilesDownloaded = noImagesDownloaded = noVideosDownloaded = noImagesSkipped = noVideosSkipped = 0
            filesDownloadedSuccessfully = []
            self.bytes_downloaded_in_backup = 0
            
            display_queue.put((self.parentApp.addToTotalDownloadSize, (sizeFiles, )))
            display_queue.put((self.parentApp.setOverallDownloadMark, ()))
            display_queue.put((self.parentApp.postStartDownloadTasks,  ()))
            
            sizeFiles = float(sizeFiles)

            addUniqueIdentifier = self.prefs.download_conflict_resolution == config.ADD_UNIQUE_IDENTIFIER
            usesImageSequenceElements = self.imageRenamePrefsFactory.usesSequenceElements()
            usesVideoSequenceElements = self.videoRenamePrefsFactory.usesSequenceElements()
            usesSequenceElements = usesVideoSequenceElements or usesImageSequenceElements
            
            usesStoredSequenceNo = (self.imageRenamePrefsFactory.usesTheSequenceElement(rn.STORED_SEQ_NUMBER) or
                                    self.videoRenamePrefsFactory.usesTheSequenceElement(rn.STORED_SEQ_NUMBER))
            sequences.setUseOfSequenceElements(
                self.imageRenamePrefsFactory.usesTheSequenceElement(rn.SESSION_SEQ_NUMBER), 
                self.imageRenamePrefsFactory.usesTheSequenceElement(rn.SEQUENCE_LETTER))
            
            # reset the progress bar to update the status of this download attempt
            progressBarText = _("%(number)s of %(total)s %(filetypes)s") % {'number':  0, 'total': noFiles, 'filetypes':self.display_file_types}
            display_queue.put((media_collection_treeview.updateProgress, (self.thread_id, 0.0, progressBarText, 0)))
            
            
            while i < noFiles:
                # if the user pauses the download, then this will be triggered
                if not self.running:
                    self.lock.acquire()
                    self.running = True
                
                if not self.ctrl:
                    self.running = False
                    self.cleanUp()
                    display_queue.close("rw")
                    return
                
                # get information about the image to deduce image name and path
                mediaFile = self.cardMedia.imagesAndVideos[fileIndex[i]][0]
                if not mediaFile.status == STATUS_DOWNLOAD_PENDING:
                    sys.stderr.write("FIXME: Thread %s is trying to download a file that it should not be!!" % self.thread_id)
                else:
                    self.bytes_downloaded_in_download = self.bytes_downloaded_in_backup = self.bytes_downloaded = 0
                    if mediaFile.isImage:
                        tempWorkingDir = self.photoTempWorkingDir
                        baseDownloadDir = photoBaseDownloadDir
                    else:
                        tempWorkingDir = self.videoTempWorkingDir
                        baseDownloadDir = videoBaseDownloadDir
                        
                    skipFile, sequence_to_use = generateSubfolderAndFileName(mediaFile)

                    if skipFile:
                        if mediaFile.isImage:
                            noImagesSkipped += 1
                        else:
                            noVideosSkipped += 1
                    else:
                        fileDownloaded = downloadFile(mediaFile, sequence_to_use)

                        if self.prefs.backup_images:
                            backed_up = backupFile(mediaFile, fileDownloaded, no_backup_devices)

                        if fileDownloaded:
                            noFilesDownloaded += 1
                            if mediaFile.isImage:
                                noImagesDownloaded += 1
                            else:
                                noVideosDownloaded += 1
                            if self.prefs.backup_images and backed_up:
                                filesDownloadedSuccessfully.append(mediaFile.fullFileName)
                            elif not self.prefs.backup_images:
                                filesDownloadedSuccessfully.append(mediaFile.fullFileName)
                        else:
                            if mediaFile.isImage:
                                noImagesSkipped += 1
                            else:
                                noVideosSkipped += 1
                                
                    #update the selction treeview in the main window with the new status of the file
                    display_queue.put((self.parentApp.update_status_post_download, (mediaFile.treerowref, )))

                percentComplete = (float(self.sizeDownloaded) / sizeFiles) * 100
                    
                if self.sizeDownloaded == sizeFiles and (totalNonErrorFiles - noFiles):
                    progressBarText = _("%(number)s of %(total)s %(filetypes)s (%(remaining)s remaining)") % {
                                        'number':  i + 1, 'total': noFiles, 'filetypes':self.display_file_types,
                                        'remaining': totalNonErrorFiles - noFiles}
                else:
                    progressBarText = _("%(number)s of %(total)s %(filetypes)s") % {'number':  i + 1, 'total': noFiles, 'filetypes':self.display_file_types}
                
                if using_gio:
                    # do not want to update the progress bar any more than it has already been updated
                    size = mediaFile.size * (no_backup_devices + 1) - self.bytes_downloaded_in_download - self.bytes_downloaded_in_backup
                else:
                    size = mediaFile.size * (no_backup_devices + 1)
                display_queue.put((media_collection_treeview.updateProgress, (self.thread_id, percentComplete, progressBarText, size)))
                
                i += 1
            
            with self.statsLock:
                self.downloadStats.adjust(self.sizeDownloaded, noImagesDownloaded, noVideosDownloaded, noImagesSkipped, noVideosSkipped, self.noWarnings, self.noErrors)
                
            if self.prefs.auto_delete:
                j = 0
                for imageOrVideo in filesDownloadedSuccessfully:
                    try:
                        os.unlink(imageOrVideo)
                        j += 1
                    except OSError, (errno, strerror):
                        logError(config.SERIOUS_ERROR,  _("Could not delete photo or video from device"),  
                                _("Photo: %(source)s\nError: %(errno)s %(strerror)s")
                                % {'source': image, 'errno': errno,  'strerror': strerror})
                    except:
                        logError(config.SERIOUS_ERROR,  _("Could not delete photo or video from device"),  
                                _("Photo: %(source)s"))
                        
                cmd_line(_("Deleted %(number)i %(filetypes)s from device") % {'number':j, 'filetypes':self.display_file_types})
                
            totalNonErrorFiles = totalNonErrorFiles - noFiles
            if totalNonErrorFiles == 0:
                all_files_downloaded = True
                
                # must manually delete these variables, or else the media cannot be unmounted (bug in some versions of pyexiv2 / exiv2)
                # for some reason directories on the device remain open with read only access, even after these steps - I don't know why
                del self.subfolderPrefsFactory, self.imageRenamePrefsFactory, self.videoSubfolderPrefsFactory, self.videoRenamePrefsFactory
                for i in self.cardMedia.imagesAndVideos:
                    i[0].metadata = None
                
            notifyAndUnmount(umountAttemptOK = all_files_downloaded)
            cmd_line(_("Download complete from %s") % self.cardMedia.prettyName(limit=0))
            display_queue.put((self.parentApp.notifyUserAllDownloadsComplete,()))
            display_queue.put((self.parentApp.resetSequences,()))
            
            if all_files_downloaded:
                self.downloadComplete = True
            else:
                self.cleanUp()
                self.downloadStarted = False
                self.running = False
                self.lock.acquire()
                if not self.ctrl:
                    # thread will restart at this point, when the program is exiting
                    # so must exit if self.ctrl indicates this

                    self.running = False
                    display_queue.close("rw")
                    return
                self.running = True
                if not createBothTempDirs():
                    return


        display_queue.put((self.parentApp.exitOnDownloadComplete, ()))
        display_queue.close("rw")

        self.cleanUp()
                
        self.running = False
        if noFiles:
            self.lock.release()
        
    
    def startStop(self):


Generated by  Doxygen 1.6.0   Back to index