// Returns:
        // - the list of files whose dates have changed
        // - a collection of feedback information for each file whose dates were changed, each row detailing the file name and how the dates were changed
        // - the number of missing Files, if any
        private List <ImageRow> GetImageRowsWithChangedDates(IProgress <ProgressBarArguments> progress, int count, TimeZoneInfo imageSetTimeZone, ObservableCollection <DateTimeFeedbackTuple> feedbackRows, out int missingFiles)
        {
            List <ImageRow> filesToAdjust = new List <ImageRow>();

            missingFiles = 0;
            for (int fileIndex = 0; fileIndex < count; ++fileIndex)
            {
                if (Token.IsCancellationRequested)
                {
                    // A cancel was requested. Clear all pending changes and abort
                    feedbackRows.Clear();
                    break;
                }

                // We will store the various times here
                ImageRow       file             = this.fileDatabase.FileTable[fileIndex];
                DateTimeOffset originalDateTime = file.DateTimeIncorporatingOffset;
                string         feedbackMessage  = string.Empty;

                try
                {
                    // Get the image (if its there), get the new dates/times, and add it to the list of images to be updated
                    // Note that if the image can't be created, we will just to the catch.
                    bool usingMetadataTimestamp = true;
                    if (file.FileExists(this.fileDatabase.FolderPath) == false)
                    {
                        // The file does not exist. Generate a feedback message
                        missingFiles++;
                    }
                    else
                    {
                        // Read the date from the file, and check to see if its different from the recorded date
                        DateTimeAdjustmentEnum dateTimeAdjustment = file.TryReadDateTimeOriginalFromMetadata(this.fileDatabase.FolderPath, imageSetTimeZone);
                        if (dateTimeAdjustment == DateTimeAdjustmentEnum.MetadataNotUsed)
                        {
                            // We couldn't read the metadata, so get a candidate date/time from the file info instead
                            file.SetDateTimeOffsetFromFileInfo(this.fileDatabase.FolderPath);
                            usingMetadataTimestamp = false;
                        }
                        DateTimeOffset rescannedDateTime = file.DateTimeIncorporatingOffset;
                        bool           sameDate          = rescannedDateTime.Date == originalDateTime.Date;
                        bool           sameTime          = rescannedDateTime.TimeOfDay == originalDateTime.TimeOfDay;
                        bool           sameUTCOffset     = rescannedDateTime.Offset == originalDateTime.Offset;

                        if (!(sameDate && sameTime && sameUTCOffset))
                        {
                            // Date has been updated - add it to the queue of files to be processed, and generate a feedback message.
                            filesToAdjust.Add(file);
                            feedbackMessage  = "\x2713"; // Checkmark
                            feedbackMessage += DateTimeHandler.ToStringDisplayDateTime(originalDateTime) + " \x2192 " + DateTimeHandler.ToStringDisplayDateTime(rescannedDateTime);
                            feedbackMessage += usingMetadataTimestamp ? " (read from metadata)" : " (read from file)";
                            feedbackRows.Add(new DateTimeFeedbackTuple(file.File, feedbackMessage));
                        }
                    }
                }
                catch (Exception exception)
                {
                    // This shouldn't happen, but just in case.
                    TracePrint.PrintMessage(string.Format("Unexpected exception processing '{0}' in DateTimeReread. {1}", file.File, exception.ToString()));
                    feedbackMessage += string.Format("\x2716 skipping: {0}", exception.Message);
                    feedbackRows.Add(new DateTimeFeedbackTuple(file.File, feedbackMessage));
                    break;
                }

                // Update the progress bar every time interval to indicate what file we are working on
                TimeSpan intervalFromLastRefresh = DateTime.Now - this.lastRefreshDateTime;
                if (intervalFromLastRefresh > Constant.ThrottleValues.ProgressBarRefreshInterval)
                {
                    int percentDone = Convert.ToInt32(fileIndex / Convert.ToDouble(count) * 100.0);
                    progress.Report(new ProgressBarArguments(percentDone, String.Format("Pass 1: Checking dates for {0} / {1} files", fileIndex, count), true, false));
                    Thread.Sleep(Constant.ThrottleValues.RenderingBackoffTime);
                    this.lastRefreshDateTime = DateTime.Now;
                }
            }
            return(filesToAdjust);
        }
Beispiel #2
0
        // Populate the database with the metadata for the selected note field
        private async Task <ObservableCollection <KeyValuePair <string, string> > > PopulateAsync(bool?metadataExtractorRBIsChecked)
        {
            // This list will hold key / value pairs that will be bound to the datagrid feedback,
            // which is the way to make those pairs appear in the data grid during background worker progress updates
            ObservableCollection <KeyValuePair <string, string> > keyValueList = new ObservableCollection <KeyValuePair <string, string> >();

            return(await Task.Run(() =>
            {
                // For each row in the database, get the image filename and try to extract the chosen metadata value.
                // If we can't decide if we want to leave the data field alone or to clear it depending on the state of the isClearIfNoMetadata (set via the checkbox)
                // Report progress as needed.
                // This tuple list will hold the id, key and value that we will want to update in the database
                string dataLabelToUpdate = this.dataLabelByLabel[this.dataFieldLabel];
                List <ColumnTuplesWithWhere> imagesToUpdate = new List <ColumnTuplesWithWhere>();
                TimeZoneInfo imageSetTimeZone = this.fileDatabase.ImageSet.GetSystemTimeZone();
                int percentDone = 0;

                double totalImages = this.fileDatabase.CountAllCurrentlySelectedFiles;
                Dictionary <string, ImageMetadata> metadata = new Dictionary <string, ImageMetadata>();
                for (int imageIndex = 0; imageIndex < totalImages; ++imageIndex)
                {
                    // Provide feedback if the operation was cancelled during the database update
                    if (Token.IsCancellationRequested == true)
                    {
                        keyValueList.Clear();
                        keyValueList.Add(new KeyValuePair <string, string>("Cancelled", "No changes were made"));
                        return keyValueList;
                    }

                    ImageRow image = this.fileDatabase.FileTable[imageIndex];
                    if (metadataExtractorRBIsChecked == true)
                    {   // MetadataExtractor specific code
                        metadata = ImageMetadataDictionary.LoadMetadata(image.GetFilePath(this.fileDatabase.FolderPath));
                    }
                    else
                    {
                        // ExifTool specific code - note that we transform results into the same dictionary structure used by the MetadataExtractor
                        string[] tags = { this.metadataFieldName };
                        metadata.Clear();
                        Dictionary <string, string> exifData = this.exifTool.FetchExifFrom(image.GetFilePath(this.fileDatabase.FolderPath), tags);
                        if (exifData.ContainsKey(tags[0]))
                        {
                            metadata.Add(tags[0], new Timelapse.Util.ImageMetadata(String.Empty, tags[0], exifData[tags[0]]));
                        }
                    }

                    if (this.ReadyToRefresh())
                    {
                        percentDone = Convert.ToInt32(imageIndex / totalImages * 100.0);
                        this.Progress.Report(new ProgressBarArguments(percentDone, String.Format("{0}/{1} images. Processing {2}", imageIndex, totalImages, image.File), true, false));
                        Thread.Sleep(Constant.ThrottleValues.RenderingBackoffTime);  // Allows the UI thread to update every now and then
                    }

                    if (metadata.ContainsKey(this.metadataFieldName) == false)
                    {
                        if (this.clearIfNoMetadata)
                        {
                            // Clear the data field if there is no metadata...
                            if (dataLabelToUpdate == Constant.DatabaseColumn.DateTime)
                            {
                                image.SetDateTimeOffsetFromFileInfo(this.fileDatabase.FolderPath);
                                imagesToUpdate.Add(image.GetDateTimeColumnTuples());
                                keyValueList.Add(new KeyValuePair <string, string>(image.File, "No metadata found - date/time reread from file"));
                            }
                            else
                            {
                                List <ColumnTuple> clearField = new List <ColumnTuple>()
                                {
                                    new ColumnTuple(this.dataLabelByLabel[this.dataFieldLabel], String.Empty)
                                };
                                imagesToUpdate.Add(new ColumnTuplesWithWhere(clearField, image.ID));
                                keyValueList.Add(new KeyValuePair <string, string>(image.File, "No metadata found - data field is cleared"));
                            }
                        }
                        else
                        {
                            keyValueList.Add(new KeyValuePair <string, string>(image.File, "No metadata found - data field remains unaltered"));
                        }

                        continue;
                    }

                    string metadataValue = metadata[this.metadataFieldName].Value;
                    ColumnTuplesWithWhere imageUpdate;
                    if (dataLabelToUpdate == Constant.DatabaseColumn.DateTime)
                    {
                        if (DateTimeHandler.TryParseMetadataDateTaken(metadataValue, imageSetTimeZone, out DateTimeOffset metadataDateTime))
                        {
                            image.SetDateTimeOffset(metadataDateTime);
                            imageUpdate = image.GetDateTimeColumnTuples();
                            keyValueList.Add(new KeyValuePair <string, string>(image.File, metadataValue));
                        }
                        else
                        {
                            keyValueList.Add(new KeyValuePair <string, string>(image.File, String.Format("'{0}' - data field remains unaltered - not a valid date/time.", metadataValue)));
                            continue;
                        }
                    }
                    else
                    {
                        imageUpdate = new ColumnTuplesWithWhere(new List <ColumnTuple>()
                        {
                            new ColumnTuple(dataLabelToUpdate, metadataValue)
                        }, image.ID);
                        keyValueList.Add(new KeyValuePair <string, string>(image.File, metadataValue));
                    }
                    imagesToUpdate.Add(imageUpdate);
                }
                this.IsAnyDataUpdated = true;
                this.Progress.Report(new ProgressBarArguments(100, String.Format("Writing metadata for {0} files. Please wait...", totalImages), false, true));
                Thread.Sleep(Constant.ThrottleValues.RenderingBackoffTime);  // Allows the UI thread to update every now and then
                this.fileDatabase.UpdateFiles(imagesToUpdate);
                return keyValueList;
            }, this.Token).ConfigureAwait(true));
        }