// 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); }
// 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)); }