public static async Task <GenerationReturn> Validate(LinkContent linkContent) { var rootDirectoryCheck = UserSettingsUtilities.ValidateLocalSiteRootDirectory(); if (!rootDirectoryCheck.Item1) { return(await GenerationReturn.Error($"Problem with Root Directory: {rootDirectoryCheck.Item2}", linkContent.ContentId)); } if (linkContent == null) { return(await GenerationReturn.Error("Link Content is Null?")); } var(createdUpdatedValid, createdUpdatedValidationMessage) = CommonContentValidation.ValidateCreatedAndUpdatedBy(linkContent, linkContent.Id < 1); if (!createdUpdatedValid) { return(await GenerationReturn.Error(createdUpdatedValidationMessage, linkContent.ContentId)); } var urlValidation = await CommonContentValidation.ValidateLinkContentLinkUrl(linkContent.Url, linkContent.ContentId); if (!urlValidation.isValid) { return(await GenerationReturn.Error(urlValidation.explanation, linkContent.ContentId)); } return(await GenerationReturn.Success("Link Content Validation Successful")); }
public static async Task <GenerationReturn> CopyCleanResizeImage(ImageContent dbEntry, IProgress <string> progress) { if (dbEntry == null) { return(await GenerationReturn.Error("Null Image Content submitted to Copy Clean and Resize")); } progress?.Report($"Starting Copy, Clean and Resize for {dbEntry.Title}"); if (string.IsNullOrWhiteSpace(dbEntry.OriginalFileName)) { return(await GenerationReturn.Error($"Image {dbEntry.Title} has no Original File", dbEntry.ContentId)); } var imageDirectory = UserSettingsSingleton.CurrentSettings().LocalSiteImageContentDirectory(dbEntry); var syncCopyResults = await FileManagement.CheckImageFileIsInMediaAndContentDirectories(dbEntry, progress); if (!syncCopyResults.HasError) { return(syncCopyResults); } CleanDisplayAndSrcSetFilesInImageDirectory(dbEntry, true, progress); ResizeForDisplayAndSrcset(new FileInfo(Path.Combine(imageDirectory.FullName, dbEntry.OriginalFileName)), false, progress); return(await GenerationReturn.Success($"{dbEntry.Title} Copied, Cleaned, Resized", dbEntry.ContentId)); }
public static async Task <GenerationReturn> Validate(MapComponentDto mapComponent) { var rootDirectoryCheck = UserSettingsUtilities.ValidateLocalSiteRootDirectory(); if (!rootDirectoryCheck.Item1) { return(await GenerationReturn.Error($"Problem with Root Directory: {rootDirectoryCheck.Item2}", mapComponent.Map.ContentId)); } var commonContentCheck = await CommonContentValidation.ValidateMapComponent(mapComponent); if (!commonContentCheck.valid) { return(await GenerationReturn.Error(commonContentCheck.explanation, mapComponent.Map.ContentId)); } var updateFormatCheck = CommonContentValidation.ValidateUpdateContentFormat(mapComponent.Map.UpdateNotesFormat); if (!updateFormatCheck.isValid) { return(await GenerationReturn.Error(updateFormatCheck.explanation, mapComponent.Map.ContentId)); } return(await GenerationReturn.Success("GeoJson Content Validation Successful")); }
public static async Task <GenerationReturn> Validate(TagExclusion toValidate) { if (toValidate == null) { return(await GenerationReturn.Error("Excluded Tag can not be empty")); } if (string.IsNullOrWhiteSpace(toValidate.Tag)) { return(await GenerationReturn.Error("Excluded Tag can not be blank")); } var validationResult = CommonContentValidation.ValidateTags(toValidate.Tag.TrimNullToEmpty()); if (!validationResult.isValid) { return(await GenerationReturn.Error(validationResult.explanation)); } var cleanedTag = Db.TagListItemCleanup(toValidate.Tag); var db = await Db.Context(); if (db.TagExclusions.Any(x => x.Id != toValidate.Id && x.Tag == cleanedTag)) { return(await GenerationReturn.Error( $"It appears that the tag '{cleanedTag}' is already in the Exclusion List?")); } return(await GenerationReturn.Success("Tag Exclusion is Valid")); }
public static async Task <GenerationReturn> Validate(ImageContent imageContent, FileInfo selectedFile) { var rootDirectoryCheck = UserSettingsUtilities.ValidateLocalSiteRootDirectory(); if (!rootDirectoryCheck.Item1) { return(await GenerationReturn.Error($"Problem with Root Directory: {rootDirectoryCheck.Item2}", imageContent.ContentId)); } var mediaArchiveCheck = UserSettingsUtilities.ValidateLocalMediaArchive(); if (!mediaArchiveCheck.Item1) { return(await GenerationReturn.Error($"Problem with Media Archive: {mediaArchiveCheck.Item2}", imageContent.ContentId)); } var commonContentCheck = await CommonContentValidation.ValidateContentCommon(imageContent); if (!commonContentCheck.valid) { return(await GenerationReturn.Error(commonContentCheck.explanation, imageContent.ContentId)); } var updateFormatCheck = CommonContentValidation.ValidateUpdateContentFormat(imageContent.UpdateNotesFormat); if (!updateFormatCheck.isValid) { return(await GenerationReturn.Error(updateFormatCheck.explanation, imageContent.ContentId)); } selectedFile.Refresh(); if (!selectedFile.Exists) { return(await GenerationReturn.Error("Selected File doesn't exist?", imageContent.ContentId)); } if (!FolderFileUtility.IsNoUrlEncodingNeeded(Path.GetFileNameWithoutExtension(selectedFile.Name))) { return(await GenerationReturn.Error("Limit File Names to A-Z a-z - . _", imageContent.ContentId)); } if (!FolderFileUtility.PictureFileTypeIsSupported(selectedFile)) { return(await GenerationReturn.Error("The file doesn't appear to be a supported file type.", imageContent.ContentId)); } if (await(await Db.Context()).ImageFilenameExistsInDatabase(selectedFile.Name, imageContent.ContentId)) { return(await GenerationReturn.Error( "This filename already exists in the database - image file names must be unique.", imageContent.ContentId)); } return(await GenerationReturn.Success("Image Content Validation Successful")); }
public static async Task <(GenerationReturn, PhotoContent)> PhotoMetadataToNewPhotoContent(FileInfo selectedFile, IProgress <string> progress, string photoContentCreatedBy = null) { selectedFile.Refresh(); if (!selectedFile.Exists) { return(await GenerationReturn.Error("File Does Not Exist?"), null); } var metadataReturn = await PhotoMetadataFromFile(selectedFile, progress); if (metadataReturn.generationReturn.HasError) { return(metadataReturn.generationReturn, null); } var toReturn = new PhotoContent(); toReturn.InjectFrom(metadataReturn.metadata); toReturn.OriginalFileName = selectedFile.Name; toReturn.ContentId = Guid.NewGuid(); toReturn.CreatedBy = string.IsNullOrWhiteSpace(photoContentCreatedBy) ? UserSettingsSingleton.CurrentSettings().DefaultCreatedBy : photoContentCreatedBy.Trim(); toReturn.CreatedOn = DateTime.Now; toReturn.Slug = SlugUtility.Create(true, toReturn.Title); toReturn.BodyContentFormat = ContentFormatDefaults.Content.ToString(); toReturn.UpdateNotesFormat = ContentFormatDefaults.Content.ToString(); var possibleTitleYear = Regex .Match(toReturn.Title, @"\A(?<possibleYear>\d\d\d\d) (?<possibleMonth>January?|February?|March?|April?|May|June?|July?|August?|September?|October?|November?|December?) .*", RegexOptions.IgnoreCase).Groups["possibleYear"].Value; if (!string.IsNullOrWhiteSpace(possibleTitleYear)) { if (int.TryParse(possibleTitleYear, out var convertedYear)) { if (convertedYear >= 1826 && convertedYear <= DateTime.Now.Year) { toReturn.Folder = convertedYear.ToString("F0"); } } } if (string.IsNullOrWhiteSpace(toReturn.Folder)) { toReturn.Folder = toReturn.PhotoCreatedOn.Year.ToString("F0"); } return(await GenerationReturn.Success($"Parsed Photo Metadata for {selectedFile.FullName} without error"), toReturn); }
public static async Task <GenerationReturn> Validate(PhotoContent photoContent, FileInfo selectedFile) { var rootDirectoryCheck = UserSettingsUtilities.ValidateLocalSiteRootDirectory(); if (!rootDirectoryCheck.Item1) { return(await GenerationReturn.Error($"Problem with Root Directory: {rootDirectoryCheck.Item2}", photoContent.ContentId)); } var mediaArchiveCheck = UserSettingsUtilities.ValidateLocalMediaArchive(); if (!mediaArchiveCheck.Item1) { return(await GenerationReturn.Error($"Problem with Media Archive: {mediaArchiveCheck.Item2}", photoContent.ContentId)); } var commonContentCheck = await CommonContentValidation.ValidateContentCommon(photoContent); if (!commonContentCheck.valid) { return(await GenerationReturn.Error(commonContentCheck.explanation, photoContent.ContentId)); } var updateFormatCheck = CommonContentValidation.ValidateUpdateContentFormat(photoContent.UpdateNotesFormat); if (!updateFormatCheck.isValid) { return(await GenerationReturn.Error(updateFormatCheck.explanation, photoContent.ContentId)); } selectedFile.Refresh(); var photoFileValidation = await CommonContentValidation.PhotoFileValidation(selectedFile, photoContent?.ContentId); if (!photoFileValidation.isValid) { return(await GenerationReturn.Error(photoFileValidation.explanation, photoContent.ContentId)); } return(await GenerationReturn.Success("Photo Content Validation Successful")); }
public static async Task <GenerationReturn> Validate(NoteContent noteContent) { var rootDirectoryCheck = UserSettingsUtilities.ValidateLocalSiteRootDirectory(); if (!rootDirectoryCheck.Item1) { return(await GenerationReturn.Error($"Problem with Root Directory: {rootDirectoryCheck.Item2}", noteContent.ContentId)); } var commonContentCheck = await CommonContentValidation.ValidateContentCommon(noteContent); if (!commonContentCheck.valid) { return(await GenerationReturn.Error(commonContentCheck.explanation, noteContent.ContentId)); } return(await GenerationReturn.Success("Note Content Validation Successful")); }
public static async Task <GenerationReturn> CheckStringForBadContentReferences(string toSearch, PointlessWaymarksContext db, IProgress <string> progress) { var extracted = new List <Guid>(); if (string.IsNullOrWhiteSpace(toSearch)) { return(await GenerationReturn.Success("No Content Ids Found")); } extracted.AddRange(BracketCodeCommon.BracketCodeContentIds(toSearch)); if (!extracted.Any()) { return(await GenerationReturn.Success("No Content Ids Found")); } progress?.Report($"Found {extracted.Count} ContentIds to check for"); var notFoundList = new List <Guid>(); foreach (var loopExtracted in extracted) { var contentLookup = await db.ContentFromContentId(loopExtracted); if (contentLookup == null) { progress?.Report($"ContentId {loopExtracted} Not Found in Db..."); notFoundList.Add(loopExtracted); } } if (notFoundList.Any()) { return(await GenerationReturn.Error( $"Invalid ContentIds in Bracket Codes - {string.Join(", ", notFoundList)}")); } return(await GenerationReturn.Success("No Invalid Content Ids Found")); }
public static async Task <GenerationReturn> Validate(PointContentDto pointContent) { var rootDirectoryCheck = UserSettingsUtilities.ValidateLocalSiteRootDirectory(); if (!rootDirectoryCheck.Item1) { return(await GenerationReturn.Error($"Problem with Root Directory: {rootDirectoryCheck.Item2}", pointContent.ContentId)); } var commonContentCheck = await CommonContentValidation.ValidateContentCommon(pointContent); if (!commonContentCheck.valid) { return(await GenerationReturn.Error(commonContentCheck.explanation, pointContent.ContentId)); } var latitudeCheck = CommonContentValidation.LatitudeValidation(pointContent.Latitude); if (!latitudeCheck.isValid) { return(await GenerationReturn.Error(latitudeCheck.explanation, pointContent.ContentId)); } var longitudeCheck = CommonContentValidation.LongitudeValidation(pointContent.Longitude); if (!longitudeCheck.isValid) { return(await GenerationReturn.Error(longitudeCheck.explanation, pointContent.ContentId)); } var elevationCheck = CommonContentValidation.ElevationValidation(pointContent.Elevation); if (!elevationCheck.isValid) { return(await GenerationReturn.Error(elevationCheck.explanation, pointContent.ContentId)); } var updateFormatCheck = CommonContentValidation.ValidateUpdateContentFormat(pointContent.UpdateNotesFormat); if (!updateFormatCheck.isValid) { return(await GenerationReturn.Error(updateFormatCheck.explanation, pointContent.ContentId)); } foreach (var loopDetails in pointContent.PointDetails) { if (loopDetails.ContentId == Guid.Empty) { return(await GenerationReturn.Error("Point Detail Data must have a valid Content Id", loopDetails.ContentId)); } if (loopDetails.PointContentId != pointContent.ContentId) { return(await GenerationReturn.Error( $"{loopDetails.DataType} Point Detail isn't assigned to the current point?", loopDetails.ContentId)); } if (string.IsNullOrWhiteSpace(loopDetails.DataType)) { return(await GenerationReturn.Error("Point Detail Data Type doesn't have a value", loopDetails.ContentId)); } if (string.IsNullOrWhiteSpace(loopDetails.StructuredDataAsJson)) { return(await GenerationReturn.Error($"{loopDetails.DataType} Point Detail doesn't have any data?", loopDetails.ContentId)); } try { var content = Db.PointDetailDataFromIdentifierAndJson(loopDetails.DataType, loopDetails.StructuredDataAsJson); var contentValidation = content.Validate(); if (!contentValidation.isValid) { return(await GenerationReturn.Error( $"{loopDetails.DataType} Point Detail: {contentValidation.validationMessage}", pointContent.ContentId)); } } catch (Exception e) { return(await GenerationReturn.Error( $"Exception loading the Structured Data for {loopDetails.DataType} Point Detail {e.Message}", pointContent.ContentId)); } } return(await GenerationReturn.Success("Point Content Validation Successful")); }
public static async Task <(GenerationReturn generationReturn, LinkMetadata metadata)> LinkMetadataFromUrl( string url, IProgress <string> progress) { if (string.IsNullOrWhiteSpace(url)) { return(await GenerationReturn.Error("No URL?"), null); } progress?.Report("Setting up and Downloading Site"); var toReturn = new LinkMetadata(); var config = Configuration.Default.WithDefaultLoader().WithJs(); var context = BrowsingContext.New(config); var document = await context.OpenAsync(url); progress?.Report("Looking for Title"); var titleString = document.Head?.Children.FirstOrDefault(x => x.TagName == "TITLE")?.TextContent; if (string.IsNullOrWhiteSpace(titleString)) { titleString = document.QuerySelector("meta[property='og:title']")?.Attributes .FirstOrDefault(x => x.LocalName == "content")?.Value; } if (string.IsNullOrWhiteSpace(titleString)) { titleString = document.QuerySelector("meta[name='DC.title']")?.Attributes .FirstOrDefault(x => x.LocalName == "content")?.Value; } if (string.IsNullOrWhiteSpace(titleString)) { titleString = document.QuerySelector("meta[name='twitter:title']")?.Attributes .FirstOrDefault(x => x.LocalName == "value")?.Value; } if (!string.IsNullOrWhiteSpace(titleString)) { toReturn.Title = titleString; } progress?.Report("Looking for Author"); var authorString = document.QuerySelector("meta[property='og:author']")?.Attributes .FirstOrDefault(x => x.LocalName == "content")?.Value; if (string.IsNullOrWhiteSpace(authorString)) { authorString = document.QuerySelector("meta[name='DC.contributor']")?.Attributes .FirstOrDefault(x => x.LocalName == "content")?.Value; } if (string.IsNullOrWhiteSpace(authorString)) { authorString = document.QuerySelector("meta[property='article:author']")?.Attributes .FirstOrDefault(x => x.LocalName == "content")?.Value; } if (string.IsNullOrWhiteSpace(authorString)) { authorString = document.QuerySelector("meta[name='author']")?.Attributes .FirstOrDefault(x => x.LocalName == "content")?.Value; } if (string.IsNullOrWhiteSpace(authorString)) { authorString = document.QuerySelector("a[rel~=\"author\"]")?.TextContent; } if (string.IsNullOrWhiteSpace(authorString)) { authorString = document.QuerySelector(".author__name")?.TextContent; } if (string.IsNullOrWhiteSpace(authorString)) { authorString = document.QuerySelector(".author_name")?.TextContent; } if (!string.IsNullOrWhiteSpace(authorString)) { toReturn.Author = authorString; } progress?.Report($"Looking for Author - Found {toReturn.Author}"); progress?.Report("Looking for Date Time"); var linkDateString = document.QuerySelector("meta[property='article:modified_time']")?.Attributes .FirstOrDefault(x => x.LocalName == "content")?.Value; if (string.IsNullOrWhiteSpace(linkDateString)) { linkDateString = document.QuerySelector("meta[property='og:updated_time']")?.Attributes .FirstOrDefault(x => x.LocalName == "content")?.Value; } if (string.IsNullOrWhiteSpace(linkDateString)) { linkDateString = document.QuerySelector("meta[property='article:published_time']")?.Attributes .FirstOrDefault(x => x.LocalName == "content")?.Value; } if (string.IsNullOrWhiteSpace(linkDateString)) { linkDateString = document.QuerySelector("meta[property='article:published_time']")?.Attributes .FirstOrDefault(x => x.LocalName == "content")?.Value; } if (string.IsNullOrWhiteSpace(linkDateString)) { linkDateString = document.QuerySelector("meta[name='DC.date.created']")?.Attributes .FirstOrDefault(x => x.LocalName == "content")?.Value; } progress?.Report($"Looking for Date Time - Found {linkDateString}"); if (!string.IsNullOrWhiteSpace(linkDateString)) { if (DateTime.TryParse(linkDateString, out var parsedDateTime)) { toReturn.LinkDate = parsedDateTime; progress?.Report($"Looking for Date Time - Parsed to {parsedDateTime}"); } else { progress?.Report("Did not parse Date Time"); } } progress?.Report("Looking for Site Name"); var siteString = document.QuerySelector("meta[property='og:site_name']")?.Attributes .FirstOrDefault(x => x.LocalName == "content")?.Value; if (string.IsNullOrWhiteSpace(siteString)) { siteString = document.QuerySelector("meta[name='DC.publisher']")?.Attributes .FirstOrDefault(x => x.LocalName == "content")?.Value; } if (string.IsNullOrWhiteSpace(siteString)) { siteString = document.QuerySelector("meta[name='twitter:site']")?.Attributes .FirstOrDefault(x => x.LocalName == "value")?.Value.Replace("@", ""); } if (!string.IsNullOrWhiteSpace(siteString)) { toReturn.Site = siteString; } progress?.Report($"Looking for Site Name - Found {toReturn.Site}"); progress?.Report("Looking for Description"); var descriptionString = document.QuerySelector("meta[name='description']")?.Attributes .FirstOrDefault(x => x.LocalName == "content")?.Value; if (string.IsNullOrWhiteSpace(descriptionString)) { descriptionString = document.QuerySelector("meta[property='og:description']")?.Attributes .FirstOrDefault(x => x.LocalName == "content")?.Value; } if (string.IsNullOrWhiteSpace(descriptionString)) { descriptionString = document.QuerySelector("meta[name='twitter:description']")?.Attributes .FirstOrDefault(x => x.LocalName == "content")?.Value; } if (!string.IsNullOrWhiteSpace(descriptionString)) { toReturn.Description = descriptionString; } progress?.Report($"Looking for Description - Found {toReturn.Description}"); return(await GenerationReturn.Success($"Parsed URL Metadata for {url} without error"), toReturn); }
public static async Task <GenerationReturn> SaveLinkToPinboard(LinkContent toSave, IProgress <string> progress) { if (string.IsNullOrWhiteSpace(UserSettingsSingleton.CurrentSettings().PinboardApiToken)) { return(await GenerationReturn.Success("No PinboardApiToken - skipping save to Pinboard", toSave.ContentId)); } var descriptionFragments = new List <string>(); if (!string.IsNullOrWhiteSpace(toSave.Site)) { descriptionFragments.Add($"Site: {toSave.Site}"); } if (toSave.LinkDate != null) { descriptionFragments.Add($"Date: {toSave.LinkDate.Value:g}"); } if (!string.IsNullOrWhiteSpace(toSave.Description)) { descriptionFragments.Add($"Description: {toSave.Description}"); } if (!string.IsNullOrWhiteSpace(toSave.Comments)) { descriptionFragments.Add($"Comments: {toSave.Comments}"); } if (!string.IsNullOrWhiteSpace(toSave.Author)) { descriptionFragments.Add($"Author: {toSave.Author}"); } var tagList = Db.TagListParse(toSave.Tags); tagList.Add(UserSettingsSingleton.CurrentSettings().SiteName); tagList = tagList.Select(x => x.Replace(" ", "-")).ToList(); progress?.Report("Setting up Pinboard"); var bookmark = new Bookmark { Url = toSave.Url, Description = toSave.Title, Extended = string.Join(" ;; ", descriptionFragments), Tags = tagList, CreatedDate = DateTime.Now, Shared = true, ToRead = false, Replace = true }; try { using var pb = new PinboardAPI(UserSettingsSingleton.CurrentSettings().PinboardApiToken); progress?.Report("Adding Pinboard Bookmark"); await pb.Posts.Add(bookmark); } catch (Exception e) { return(await GenerationReturn.Error("Trouble Saving to Pinboard", toSave.ContentId, e)); } progress?.Report("Pinboard Bookmark Complete"); return(await GenerationReturn.Success("Saved to Pinboard", toSave.ContentId)); }
public static async Task <GenerationReturn> CheckForBadContentReferences(IContentCommon content, PointlessWaymarksContext db, IProgress <string> progress) { progress?.Report($"Checking ContentIds for {content.Title}"); var extracted = new List <Guid>(); if (content.MainPicture != null && content.MainPicture != content.ContentId) { extracted.Add(content.MainPicture.Value); } var toSearch = string.Empty; toSearch += content.BodyContent + content.Summary; if (content is IUpdateNotes updateContent) { toSearch += updateContent.UpdateNotes; } if (content is PointContentDto pointDto) { pointDto.PointDetails.ForEach(x => toSearch += x.StructuredDataAsJson); } if (content is PointContent point) { (await Db.PointDetailsForPoint(point.ContentId, db)).ForEach(x => toSearch += x.StructuredDataAsJson); } if (string.IsNullOrWhiteSpace(toSearch) && !extracted.Any()) { return(await GenerationReturn.Success( $"{Db.ContentTypeString(content)} {content.Title} - No Content Ids Found", content.ContentId)); } extracted.AddRange(BracketCodeCommon.BracketCodeContentIds(toSearch)); if (!extracted.Any()) { return(await GenerationReturn.Success( $"{Db.ContentTypeString(content)} {content.Title} - No Content Ids Found", content.ContentId)); } progress?.Report($"Found {extracted.Count} ContentIds to check for {content.Title}"); var notFoundList = new List <Guid>(); foreach (var loopExtracted in extracted) { var contentLookup = await db.ContentFromContentId(loopExtracted); if (contentLookup != null) { continue; } if (await db.MapComponents.AnyAsync(x => x.ContentId == loopExtracted)) { continue; } progress?.Report($"ContentId {loopExtracted} Not Found in Db..."); notFoundList.Add(loopExtracted); } if (notFoundList.Any()) { return(await GenerationReturn.Error( $"{Db.ContentTypeString(content)} {content.Title} has " + $"Invalid ContentIds in Bracket Codes - {string.Join(", ", notFoundList)}", content.ContentId)); } return(await GenerationReturn.Success( $"{Db.ContentTypeString(content)} {content.Title} - No Invalid Content Ids Found")); }
public static async Task <(GenerationReturn generationReturn, PhotoMetadata metadata)> PhotoMetadataFromFile( FileInfo selectedFile, IProgress <string> progress) { progress?.Report("Starting Metadata Processing"); selectedFile.Refresh(); if (!selectedFile.Exists) { return(await GenerationReturn.Error("File Does Not Exist?"), null); } var toReturn = new PhotoMetadata(); progress?.Report("Getting Directories"); var exifSubIfDirectory = ImageMetadataReader.ReadMetadata(selectedFile.FullName) .OfType <ExifSubIfdDirectory>().FirstOrDefault(); var exifDirectory = ImageMetadataReader.ReadMetadata(selectedFile.FullName).OfType <ExifIfd0Directory>() .FirstOrDefault(); var iptcDirectory = ImageMetadataReader.ReadMetadata(selectedFile.FullName).OfType <IptcDirectory>() .FirstOrDefault(); var gpsDirectory = ImageMetadataReader.ReadMetadata(selectedFile.FullName).OfType <GpsDirectory>() .FirstOrDefault(); var xmpDirectory = ImageMetadataReader.ReadMetadata(selectedFile.FullName).OfType <XmpDirectory>() .FirstOrDefault(); toReturn.PhotoCreatedBy = exifDirectory?.GetDescription(ExifDirectoryBase.TagArtist) ?? string.Empty; if (string.IsNullOrWhiteSpace(toReturn.PhotoCreatedBy)) { toReturn.PhotoCreatedBy = xmpDirectory?.XmpMeta?.GetArrayItem(XmpConstants.NsDC, "creator", 1)?.Value ?? string.Empty; } if (string.IsNullOrWhiteSpace(toReturn.PhotoCreatedBy)) { toReturn.PhotoCreatedBy = iptcDirectory?.GetDescription(IptcDirectory.TagByLine) ?? string.Empty; } var createdOn = exifSubIfDirectory?.GetDescription(ExifDirectoryBase.TagDateTimeOriginal); if (string.IsNullOrWhiteSpace(createdOn)) { var gpsDateTime = DateTime.MinValue; if (gpsDirectory?.TryGetGpsDate(out gpsDateTime) ?? false) { if (gpsDateTime != DateTime.MinValue) { toReturn.PhotoCreatedOn = gpsDateTime.ToLocalTime(); } } else { toReturn.PhotoCreatedOn = DateTime.Now; } } else { var createdOnParsed = DateTime.TryParseExact( exifSubIfDirectory.GetDescription(ExifDirectoryBase.TagDateTimeOriginal), "yyyy:MM:dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out var parsedDate); toReturn.PhotoCreatedOn = createdOnParsed ? parsedDate : DateTime.Now; } var isoString = exifSubIfDirectory?.GetDescription(ExifDirectoryBase.TagIsoEquivalent); if (!string.IsNullOrWhiteSpace(isoString)) { toReturn.Iso = int.Parse(isoString); } toReturn.CameraMake = exifDirectory?.GetDescription(ExifDirectoryBase.TagMake) ?? string.Empty; toReturn.CameraModel = exifDirectory?.GetDescription(ExifDirectoryBase.TagModel) ?? string.Empty; toReturn.FocalLength = exifSubIfDirectory?.GetDescription(ExifDirectoryBase.TagFocalLength) ?? string.Empty; toReturn.Lens = exifSubIfDirectory?.GetDescription(ExifDirectoryBase.TagLensModel) ?? string.Empty; if (toReturn.Lens == string.Empty || toReturn.Lens == "----") { toReturn.Lens = xmpDirectory?.XmpMeta?.GetProperty(XmpConstants.NsExifAux, "Lens")?.Value ?? string.Empty; } if (toReturn.Lens == string.Empty || toReturn.Lens == "----") { toReturn.Lens = xmpDirectory?.XmpMeta?.GetProperty(XmpConstants.NsCameraraw, "LensProfileName")?.Value ?? string.Empty; if (toReturn.Lens.StartsWith("Adobe (")) { toReturn.Lens = toReturn.Lens.Substring(7, toReturn.Lens.Length - 7); if (toReturn.Lens.EndsWith(")")) { toReturn.Lens = toReturn.Lens.Substring(0, toReturn.Lens.Length - 1); } } } if (toReturn.Lens == "----") { toReturn.Lens = string.Empty; } toReturn.Aperture = exifSubIfDirectory?.GetDescription(ExifDirectoryBase.TagAperture) ?? string.Empty; toReturn.License = exifDirectory?.GetDescription(ExifDirectoryBase.TagCopyright) ?? string.Empty; if (string.IsNullOrWhiteSpace(toReturn.License)) { toReturn.License = xmpDirectory?.XmpMeta?.GetArrayItem(XmpConstants.NsDC, "rights", 1)?.Value ?? string.Empty; } if (string.IsNullOrWhiteSpace(toReturn.License)) { toReturn.License = iptcDirectory?.GetDescription(IptcDirectory.TagCopyrightNotice) ?? string.Empty; } var shutterValue = new Rational(); if (exifSubIfDirectory?.TryGetRational(37377, out shutterValue) ?? false) { toReturn.ShutterSpeed = ExifHelpers.ShutterSpeedToHumanReadableString(shutterValue); } else { toReturn.ShutterSpeed = string.Empty; } //The XMP data - vs the IPTC - will hold the full Title for a very long title (the IPTC will be truncated) - //for a 'from Lightroom with no other concerns' export Title makes the most sense, but there are other possible //metadata fields to pull from that could be relevant in other contexts. toReturn.Title = xmpDirectory?.XmpMeta?.GetArrayItem(XmpConstants.NsDC, "title", 1)?.Value; if (string.IsNullOrWhiteSpace(toReturn.Title)) { toReturn.Title = iptcDirectory?.GetDescription(IptcDirectory.TagObjectName) ?? string.Empty; } //Use a variety of guess on common file names and make that the title - while this could result in an initial title //like DSC001 style out of camera names but after having experimented with loading files I think 'default' is better //than an invalid blank. if (string.IsNullOrWhiteSpace(toReturn.Title)) { toReturn.Title = Path.GetFileNameWithoutExtension(selectedFile.Name).Replace("-", " ").Replace("_", " ") .SplitCamelCase(); } toReturn.Summary = iptcDirectory?.GetDescription(IptcDirectory.TagObjectName) ?? string.Empty; //2020/3/22 - Process out a convention that I have used for more than a decade of pre-2020s do yyMM at the start of a photo title or in the //2020s yyyy MM at the start - hopefully this is matched specifically enough not to accidentally trigger on photos without this info... The //base case of not matching this convention is that the year and month from photo created on are added to the front of the title or if the //title is blank the photo created on is put as a timestamp into the title. One of the ideas here is to give photos as much of a chance //as possible to have valid data for an automated import even when a detail like timestamp title is probably better replaced with something //more descriptive. if (!string.IsNullOrWhiteSpace(toReturn.Title) && toReturn.Title.StartsWith("2")) { var possibleTitleDate = Regex.Match(toReturn.Title, @"\A(?<possibleDate>\d\d\d\d[\s-]\d\d[\s-]*).*", RegexOptions.IgnoreCase).Groups["possibleDate"].Value; if (!string.IsNullOrWhiteSpace(possibleTitleDate)) { try { var tempDate = new DateTime(int.Parse(possibleTitleDate.Substring(0, 4)), int.Parse(possibleTitleDate.Substring(5, 2)), 1); toReturn.Summary = $"{toReturn.Title.Substring(possibleTitleDate.Length, toReturn.Title.Length - possibleTitleDate.Length)}"; toReturn.Title = $"{tempDate:yyyy} {tempDate:MMMM} {toReturn.Title.Substring(possibleTitleDate.Length, toReturn.Title.Length - possibleTitleDate.Length)}"; progress?.Report("Title updated based on 2yyy MM start pattern for file name"); } catch { progress?.Report("Did not successfully parse 2yyy MM start pattern for file name"); } } } else if (!string.IsNullOrWhiteSpace(toReturn.Title) && (toReturn.Title.StartsWith("0") || toReturn.Title.StartsWith("1"))) { try { if (Regex.IsMatch(toReturn.Title, @"\A[01]\d\d\d\s.*", RegexOptions.IgnoreCase)) { var year = int.Parse(toReturn.Title.Substring(0, 2)); var month = int.Parse(toReturn.Title.Substring(2, 2)); var tempDate = year < 20 ? new DateTime(2000 + year, month, 1) : new DateTime(1900 + year, month, 1); toReturn.Summary = $"{toReturn.Title.Substring(5, toReturn.Title.Length - 5)}"; toReturn.Title = $"{tempDate:yyyy} {tempDate:MMMM} {toReturn.Title.Substring(5, toReturn.Title.Length - 5)}"; progress?.Report("Title updated based on YYMM start pattern for file name"); } } catch { progress?.Report("Did not successfully parse YYMM start pattern for file name"); } } else if (Regex.IsMatch(toReturn.Title, @".*[\s-][01]\d\d\d\z", RegexOptions.IgnoreCase)) { try { var year = int.Parse(toReturn.Title.Substring(toReturn.Title.Length - 4, 2)); var month = int.Parse(toReturn.Title.Substring(toReturn.Title.Length - 2, 2)); var tempDate = year < 20 ? new DateTime(2000 + year, month, 1) : new DateTime(1900 + year, month, 1); toReturn.Summary = $"{toReturn.Title.Substring(0, toReturn.Title.Length - 5)}"; toReturn.Title = $"{tempDate:yyyy} {tempDate:MMMM} {toReturn.Title.Substring(0, toReturn.Title.Length - 5)}"; progress?.Report("Title updated based on YYMM end pattern for file name"); } catch { progress?.Report("Did not successfully parse YYMM end pattern for file name"); } } else if (Regex.IsMatch(toReturn.Title, @".*[\s-]\d\d\d\d[\s-]\d\d\z", RegexOptions.IgnoreCase)) { var possibleTitleDate = Regex.Match(toReturn.Title, @".*[\s-](?<possibleDate>\d\d\d\d[\s-]\d\d)\z", RegexOptions.IgnoreCase) .Groups["possibleDate"].Value; if (!string.IsNullOrWhiteSpace(possibleTitleDate)) { try { var tempDate = new DateTime(int.Parse(possibleTitleDate.Substring(0, 4)), int.Parse(possibleTitleDate.Substring(5, 2)), 1); toReturn.Summary = $"{toReturn.Title.Substring(0, toReturn.Title.Length - possibleTitleDate.Length)}"; toReturn.Title = $"{tempDate:yyyy} {tempDate:MMMM} {toReturn.Title.Substring(0, toReturn.Title.Length - possibleTitleDate.Length)}"; progress?.Report("Title updated based on 2yyy MM end pattern for file name"); } catch { progress?.Report("Did not successfully parse 2yyy MM end pattern for file name"); } } } else { toReturn.Title = string.IsNullOrWhiteSpace(toReturn.Title) ? toReturn.PhotoCreatedOn.ToString("yyyy MMMM dd h-mm-ss tt") : $"{toReturn.PhotoCreatedOn:yyyy} {toReturn.PhotoCreatedOn:MMMM} {toReturn.Title}"; } //Order is important here - the title supplies the summary in the code above - but overwrite that if there is a //description. var description = exifDirectory?.GetDescription(ExifDirectoryBase.TagImageDescription) ?? string.Empty; if (!string.IsNullOrWhiteSpace(description)) { toReturn.Summary = description; } //Add a trailing . to the summary if it doesn't end with ! ? . if (!toReturn.Summary.EndsWith(".") && !toReturn.Summary.EndsWith("!") && !toReturn.Summary.EndsWith("?")) { toReturn.Summary = $"{toReturn.Summary}."; } //Remove multi space from title and summary if (!string.IsNullOrWhiteSpace(toReturn.Title)) { toReturn.Title = Regex.Replace(toReturn.Title, @"\s+", " ").TrimNullToEmpty(); } if (!string.IsNullOrWhiteSpace(toReturn.Summary)) { toReturn.Summary = Regex.Replace(toReturn.Summary, @"\s+", " ").TrimNullToEmpty(); } var xmpSubjectKeywordList = new List <string>(); var xmpSubjectArrayItemCount = xmpDirectory?.XmpMeta?.CountArrayItems(XmpConstants.NsDC, "subject"); if (xmpSubjectArrayItemCount != null) { for (var i = 1; i <= xmpSubjectArrayItemCount; i++) { var subjectArrayItem = xmpDirectory?.XmpMeta?.GetArrayItem(XmpConstants.NsDC, "subject", i); if (subjectArrayItem == null || string.IsNullOrWhiteSpace(subjectArrayItem.Value)) { continue; } xmpSubjectKeywordList.AddRange(subjectArrayItem.Value.Replace(";", ",").Split(",") .Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim())); } } xmpSubjectKeywordList = xmpSubjectKeywordList.Distinct().ToList(); var keywordTagList = new List <string>(); var keywordValue = iptcDirectory?.GetDescription(IptcDirectory.TagKeywords)?.Replace(";", ",") ?? string.Empty; if (!string.IsNullOrWhiteSpace(keywordValue)) { keywordTagList.AddRange(keywordValue.Replace(";", ",").Split(",") .Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim())); } if (xmpSubjectKeywordList.Count == 0 && keywordTagList.Count == 0) { toReturn.Tags = string.Empty; } else if (xmpSubjectKeywordList.Count >= keywordTagList.Count) { toReturn.Tags = string.Join(",", xmpSubjectKeywordList); } else { toReturn.Tags = string.Join(",", keywordTagList); } if (!string.IsNullOrWhiteSpace(toReturn.Tags)) { toReturn.Tags = Db.TagListJoin(Db.TagListParse(toReturn.Tags)); } return(await GenerationReturn.Success($"Parsed Photo Metadata for {selectedFile.FullName} without error"), toReturn); }
public static async Task <GenerationReturn> Validate(LineContent lineContent) { var rootDirectoryCheck = UserSettingsUtilities.ValidateLocalSiteRootDirectory(); if (!rootDirectoryCheck.Item1) { return(await GenerationReturn.Error($"Problem with Root Directory: {rootDirectoryCheck.Item2}", lineContent.ContentId)); } var commonContentCheck = await CommonContentValidation.ValidateContentCommon(lineContent); if (!commonContentCheck.valid) { return(await GenerationReturn.Error(commonContentCheck.explanation, lineContent.ContentId)); } var updateFormatCheck = CommonContentValidation.ValidateUpdateContentFormat(lineContent.UpdateNotesFormat); if (!updateFormatCheck.isValid) { return(await GenerationReturn.Error(updateFormatCheck.explanation, lineContent.ContentId)); } try { var serializer = GeoJsonSerializer.Create(SpatialHelpers.Wgs84GeometryFactory(), 3); using var stringReader = new StringReader(lineContent.Line); using var jsonReader = new JsonTextReader(stringReader); var featureCollection = serializer.Deserialize <FeatureCollection>(jsonReader); if (featureCollection.Count < 1) { return(await GenerationReturn.Error( "The GeoJson for the line appears to have an empty Feature Collection?", lineContent.ContentId)); } if (featureCollection.Count > 1) { return(await GenerationReturn.Error( "The GeoJson for the line appears to contain multiple elements? It should only contain 1 line...", lineContent.ContentId)); } if (featureCollection[0].Geometry is not LineString) { return(await GenerationReturn.Error( "The GeoJson for the line has one element but it isn't a LineString?", lineContent.ContentId)); } var lineString = featureCollection[0].Geometry as LineString; if (lineString == null || lineString.Count < 1 || lineString.Length == 0) { return(await GenerationReturn.Error("The LineString doesn't have any points or is zero length?", lineContent.ContentId)); } } catch (Exception e) { return(await GenerationReturn.Error( $"Error parsing the FeatureCollection and/or problems checking the LineString {e.Message}", lineContent.ContentId)); } return(await GenerationReturn.Success("Line Content Validation Successful")); }