// Populate the database with the metadata for the selected note field private async Task <ObservableCollection <Tuple <string, string, string> > > PopulateAsync(MetadataToolEnum metadataToolSelected) { // 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 <Tuple <string, string, string> > feedbackData = new ObservableCollection <Tuple <string, string, string> >(); // if there are no metadata / label pairs, we are done. if (this.MetadataGrid.SelectedMetadata.Count == 0) { // Catch the case where there are no selected pairs, at least for now. feedbackData.Clear(); feedbackData.Add(new Tuple <string, string, string>("Nothing was selected", "", "No changes were made")); return(feedbackData); } 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 List <ColumnTuplesWithWhere> imagesToUpdate = new List <ColumnTuplesWithWhere>(); TimeZoneInfo imageSetTimeZone = DateTimeHandler.GetNeutralTimeZone(); int percentDone = 0; double totalImages = this.FileDatabase.CountAllCurrentlySelectedFiles; Dictionary <string, ImageMetadata> metadata = new Dictionary <string, ImageMetadata>(); string[] tags = this.MetadataGrid.SelectedTags;// Only needed by ExifTool, but cheap to get for (int imageIndex = 0; imageIndex < totalImages; ++imageIndex) { // Provide feedback if the operation was cancelled during the database update if (Token.IsCancellationRequested == true) { feedbackData.Clear(); feedbackData.Add(new Tuple <string, string, string>("Cancelled", "", "No changes were made")); return feedbackData; } ImageRow image = this.FileDatabase.FileTable[imageIndex]; if (metadataToolSelected == MetadataToolEnum.MetadataExtractor) { // MetadataExtractor specific code metadata = ImageMetadataDictionary.LoadMetadata(image.GetFilePath(this.FileDatabase.FolderPath)); } else // if metadataToolSelected == MetadataToolEnum.ExifTool { // ExifTool specific code - note that we transform results into the same dictionary structure used by the MetadataExtractor // Unlike MetadataExtractor, ExifTool returns TagName instad of Directory.TagName (I think - but does that mean it would break on duplicate values? metadata.Clear(); Dictionary <string, string> exifData = this.MetadataGrid.ExifToolManager.FetchExifFrom(image.GetFilePath(this.FileDatabase.FolderPath), tags); foreach (string tag in tags) { if (exifData.ContainsKey(tag)) { metadata.Add(tag, new Timelapse.Util.ImageMetadata(String.Empty, tag, exifData[tag])); } } } // At this point, the metadata Key should be the tag name, rather than Directory.TagName // (see ImageMetadataDiction.LoadDictionary to change it back so the key is the directory.name. I think Exif never returns the directory name, so thats ok too. 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 } string dataLabelToUpdate = ""; foreach (KeyValuePair <string, string> kvp in this.MetadataGrid.SelectedMetadata) { string metadataTag = kvp.Key; dataLabelToUpdate = kvp.Value; if (false == metadata.ContainsKey(metadataTag)) { // This just skips this metadata as it was not found in the file's metadata // However, we still need to supply feedback and (if the user has asked for that option) to clear the data field if (this.clearIfNoMetadata) { List <ColumnTuple> clearField = new List <ColumnTuple>() { new ColumnTuple(dataLabelToUpdate, String.Empty) }; imagesToUpdate.Add(new ColumnTuplesWithWhere(clearField, image.ID)); feedbackData.Add(new Tuple <string, string, string>(image.File, metadataTag, "No metadata found - data field cleared")); } else { feedbackData.Add(new Tuple <string, string, string>(image.File, metadataTag, "No metadata found - data field unchanged")); } continue; } string metadataValue = metadata[metadataTag].Value; ColumnTuplesWithWhere imageUpdate; if (this.useDateMetadataOnly) { if (DateTimeHandler.TryParseMetadataDateTaken(metadataValue, imageSetTimeZone, out DateTimeOffset metadataDateTime)) { image.SetDateTimeOffset(metadataDateTime); imageUpdate = image.GetDateTimeColumnTuples(); feedbackData.Add(new Tuple <string, string, string>(image.File, metadataTag, metadataValue)); } else { feedbackData.Add(new Tuple <string, string, string>(image.File, metadataTag, String.Format("Data field unchanged - '{0}' is not a valid date/time.", metadataValue))); continue; } } else { imageUpdate = new ColumnTuplesWithWhere(new List <ColumnTuple>() { new ColumnTuple(dataLabelToUpdate, metadataValue) }, image.ID); feedbackData.Add(new Tuple <string, string, string>(image.File, metadataTag, 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 feedbackData; }, this.Token).ConfigureAwait(true)); }
// 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. // Report progress as needed. // This tuple list will hold the id, key and value that we will want to update in the database List <ColumnTuplesWithWhere> imagesToUpdate = new List <ColumnTuplesWithWhere>(); TimeZoneInfo imageSetTimeZone = DateTimeHandler.GetNeutralTimeZone(); int percentDone = 0; double totalFiles = this.fileDatabase.CountAllCurrentlySelectedFiles; Dictionary <string, ImageMetadata> metadata = new Dictionary <string, ImageMetadata>(); List <ImageRow> filesToAdjust = new List <ImageRow>(); // Start up the progress bar, so it shows something as even small data sets will have a delay in it. this.Progress.Report(new ProgressBarArguments(percentDone, "Initializing...", true, false)); Thread.Sleep(Constant.ThrottleValues.RenderingBackoffTime); // Allows the UI thread to update every now and then int metadataUpdateCount = 0; for (int imageIndex = 0; imageIndex < totalFiles; ++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 / totalFiles * 100.0); this.Progress.Report(new ProgressBarArguments(percentDone, String.Format("{0}/{1} images. Processing {2}", imageIndex, totalFiles, 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) { keyValueList.Add(new KeyValuePair <string, string>(image.File, String.Format("Data field unchanged - file is missing metatdata {0} ", this.metadataFieldName))); // System.Diagnostics.Debug.Print(String.Format("{0}: No metadata", image.File)); continue; } metadataUpdateCount++; string metadataValue = metadata[this.metadataFieldName].Value; ColumnTuplesWithWhere imageUpdate; 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("Data field unchanged - '{0}' is not a valid date/time.", metadataValue))); continue; } imagesToUpdate.Add(imageUpdate); } this.IsAnyDataUpdated = true; this.Progress.Report(new ProgressBarArguments(100, String.Format("Writing metadata for the {0}/{1} files that contain this metadata field. Please wait...", metadataUpdateCount, totalFiles), 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)); }
// 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)); }